@ca-plant-list/ca-plant-list 0.4.18 → 0.4.19
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/data/inattaxonphotos.csv +4 -0
- package/data/text/Polypodium-calirhiza.md +1 -0
- package/data/text/Polypodium-scouleri.md +1 -0
- package/lib/basepagerenderer.js +3 -3
- package/lib/ebook/images.js +5 -5
- package/lib/ebook/pages/page_list_species.js +1 -1
- package/lib/ebook/pages/taxonpage.js +2 -2
- package/lib/ebook/plantbook.js +1 -1
- package/lib/families.js +13 -13
- package/lib/genera.js +1 -1
- package/lib/htmltaxon.js +16 -7
- package/lib/index.d.ts +15 -0
- package/lib/pagerenderer.js +223 -220
- package/lib/photo.js +61 -31
- package/lib/taxa.js +2 -2
- package/lib/taxon.js +6 -3
- package/lib/tools/rpi.js +3 -4
- package/lib/web/pagetaxon.js +4 -6
- package/package.json +5 -2
- package/types/classes.d.ts +1 -28
- package/lib/inat_photo.js +0 -43
package/data/inattaxonphotos.csv
CHANGED
@@ -4044,6 +4044,10 @@ Oxalis pes-caprae,6503168,jpeg,cc-by-nc,James Bailey
|
|
4044
4044
|
Oxalis pilosa,8865145,jpg,cc-by-nc,Carol Blaney
|
4045
4045
|
Oxalis pilosa,8865151,jpg,cc-by-nc,Carol Blaney
|
4046
4046
|
Oxalis pilosa,8865149,jpg,cc-by-nc,Carol Blaney
|
4047
|
+
Oxalis smalliana,218331103,jpeg,cc-by-nc,Sadie Hickey
|
4048
|
+
Oxalis smalliana,353081201,jpg,cc-by-nc,ardouglas
|
4049
|
+
Oxalis smalliana,218331090,jpeg,cc-by-nc,Sadie Hickey
|
4050
|
+
Oxalis smalliana,294594629,jpeg,cc-by,Ed Alverson
|
4047
4051
|
Packera breweri,37358638,jpg,cc-by-nc,Stacie Wolny
|
4048
4052
|
Packera breweri,16970444,jpg,cc-by-nc,Stacie Wolny
|
4049
4053
|
Packera breweri,37358664,jpg,cc-by-nc,Stacie Wolny
|
@@ -0,0 +1 @@
|
|
1
|
+
Frond narrows at base (not triangle shaped). Sori not sunken.
|
@@ -0,0 +1 @@
|
|
1
|
+
Frond not hairy on center rib. Frond brittle.
|
package/lib/basepagerenderer.js
CHANGED
@@ -6,8 +6,8 @@ import { GlossaryPages } from "./web/glossarypages.js";
|
|
6
6
|
class BasePageRenderer {
|
7
7
|
/**
|
8
8
|
* @param {string} outputDir
|
9
|
-
* @param {Taxa} taxa
|
10
|
-
* @param {
|
9
|
+
* @param {import("./taxa.js").Taxa} taxa
|
10
|
+
* @param {import("./htmltaxon.js").TaxaColDef[]} [familyCols]
|
11
11
|
*/
|
12
12
|
static renderBasePages(outputDir, taxa, familyCols) {
|
13
13
|
const siteGenerator = new Jekyll(outputDir);
|
@@ -30,7 +30,7 @@ class BasePageRenderer {
|
|
30
30
|
|
31
31
|
/**
|
32
32
|
* @param {string} outputDir
|
33
|
-
* @param {Taxa} taxa
|
33
|
+
* @param {import("./taxa.js").Taxa} taxa
|
34
34
|
*/
|
35
35
|
static renderTools(outputDir, taxa) {
|
36
36
|
const names = [];
|
package/lib/ebook/images.js
CHANGED
@@ -23,7 +23,7 @@ class Images {
|
|
23
23
|
}
|
24
24
|
|
25
25
|
/**
|
26
|
-
* @param {Taxon[]} taxa
|
26
|
+
* @param {import("../taxon.js").Taxon[]} taxa
|
27
27
|
*/
|
28
28
|
async createImages(taxa) {
|
29
29
|
const meter = new ProgressMeter("processing photos", taxa.length);
|
@@ -102,7 +102,7 @@ class Images {
|
|
102
102
|
}
|
103
103
|
|
104
104
|
/**
|
105
|
-
* @param {Photo} photo
|
105
|
+
* @param {import("../photo.js").Photo} photo
|
106
106
|
* @returns {string}
|
107
107
|
*/
|
108
108
|
getCompressedFilePath(photo) {
|
@@ -110,7 +110,7 @@ class Images {
|
|
110
110
|
}
|
111
111
|
|
112
112
|
/**
|
113
|
-
* @param {Photo} photo
|
113
|
+
* @param {import("../photo.js").Photo} photo
|
114
114
|
* @returns {string}
|
115
115
|
*/
|
116
116
|
getCompressedImageName(photo) {
|
@@ -118,8 +118,8 @@ class Images {
|
|
118
118
|
}
|
119
119
|
|
120
120
|
/**
|
121
|
-
* @param {Taxon} taxon
|
122
|
-
* @returns {Photo[]}
|
121
|
+
* @param {import("../taxon.js").Taxon} taxon
|
122
|
+
* @returns {import("../photo.js").Photo[]}
|
123
123
|
*/
|
124
124
|
static getTaxonPhotos(taxon) {
|
125
125
|
const photos = taxon
|
@@ -13,7 +13,7 @@ class TaxonPage extends EBookPage {
|
|
13
13
|
|
14
14
|
/**
|
15
15
|
* @param {string} outputDir
|
16
|
-
* @param {Taxon} taxon
|
16
|
+
* @param {import("../../taxon.js").Taxon} taxon
|
17
17
|
* @param {Images} images
|
18
18
|
*/
|
19
19
|
constructor(outputDir, taxon, images) {
|
@@ -69,7 +69,7 @@ class TaxonPage extends EBookPage {
|
|
69
69
|
src: `i/${this.#images.getCompressedImageName(photo)}`,
|
70
70
|
style: "max-width:" + dimensions.width + "px",
|
71
71
|
});
|
72
|
-
const caption =
|
72
|
+
const caption = photo.getAttribution();
|
73
73
|
if (caption) {
|
74
74
|
img += XHTML.textElement("figcaption", caption);
|
75
75
|
}
|
package/lib/ebook/plantbook.js
CHANGED
package/lib/families.js
CHANGED
@@ -19,7 +19,7 @@ class Family {
|
|
19
19
|
}
|
20
20
|
|
21
21
|
/**
|
22
|
-
* @param {Taxon} taxon
|
22
|
+
* @param {import("./taxon.js").Taxon} taxon
|
23
23
|
*/
|
24
24
|
addTaxon(taxon) {
|
25
25
|
if (!this.#data.taxa) {
|
@@ -68,7 +68,7 @@ class Families {
|
|
68
68
|
|
69
69
|
getFamilies() {
|
70
70
|
return Object.values(this.#families).sort((a, b) =>
|
71
|
-
a.getName().localeCompare(b.getName())
|
71
|
+
a.getName().localeCompare(b.getName()),
|
72
72
|
);
|
73
73
|
}
|
74
74
|
|
@@ -81,7 +81,7 @@ class Families {
|
|
81
81
|
|
82
82
|
/**
|
83
83
|
* @param {string} outputDir
|
84
|
-
* @param {
|
84
|
+
* @param {import("./htmltaxon.js").TaxaColDef[]} [taxaColumns]
|
85
85
|
*/
|
86
86
|
renderPages(outputDir, taxaColumns) {
|
87
87
|
new PageFamilyList(outputDir, this.#families).render(taxaColumns);
|
@@ -109,7 +109,7 @@ class PageFamilyList extends GenericPage {
|
|
109
109
|
}
|
110
110
|
|
111
111
|
/**
|
112
|
-
* @param {
|
112
|
+
* @param {import("./htmltaxon.js").TaxaColDef[]} [taxaColumns]
|
113
113
|
*/
|
114
114
|
render(taxaColumns) {
|
115
115
|
let html = this.getDefaultIntro();
|
@@ -121,13 +121,13 @@ class PageFamilyList extends GenericPage {
|
|
121
121
|
|
122
122
|
// Render the section page.
|
123
123
|
new PageSection(this.getOutputDir(), name, taxa).render(
|
124
|
-
taxaColumns
|
124
|
+
taxaColumns,
|
125
125
|
);
|
126
126
|
|
127
127
|
// Render the link.
|
128
128
|
const href = "./" + name + ".html";
|
129
129
|
sectionLinks.push(
|
130
|
-
HTML.getLink(href, name) + " (" + taxa.length + ")"
|
130
|
+
HTML.getLink(href, name) + " (" + taxa.length + ")",
|
131
131
|
);
|
132
132
|
}
|
133
133
|
html += HTML.wrap("ul", HTML.arrayToLI(sectionLinks), {
|
@@ -150,7 +150,7 @@ class PageFamilyList extends GenericPage {
|
|
150
150
|
}
|
151
151
|
let cols = HTML.wrap(
|
152
152
|
"td",
|
153
|
-
HTML.getLink("./" + family.getFileName(), family.getName())
|
153
|
+
HTML.getLink("./" + family.getFileName(), family.getName()),
|
154
154
|
);
|
155
155
|
cols += HTML.wrap("td", taxa.length, { class: "right" });
|
156
156
|
html += HTML.wrap("tr", cols);
|
@@ -176,7 +176,7 @@ class PageFamily extends GenericPage {
|
|
176
176
|
}
|
177
177
|
|
178
178
|
/**
|
179
|
-
* @param {
|
179
|
+
* @param {import("./htmltaxon.js").TaxaColDef[]} [columns]
|
180
180
|
*/
|
181
181
|
render(columns) {
|
182
182
|
let html = this.getDefaultIntro();
|
@@ -184,7 +184,7 @@ class PageFamily extends GenericPage {
|
|
184
184
|
html += HTML.wrap(
|
185
185
|
"div",
|
186
186
|
Jepson.getEFloraLink(this.#family.getJepsonID()),
|
187
|
-
{ class: "section" }
|
187
|
+
{ class: "section" },
|
188
188
|
);
|
189
189
|
|
190
190
|
html += HTMLTaxon.getTaxaTable(this.#family.getTaxa(), columns);
|
@@ -199,7 +199,7 @@ class PageSection extends GenericPage {
|
|
199
199
|
/**
|
200
200
|
* @param {string} outputDir
|
201
201
|
* @param {string} name
|
202
|
-
* @param {Taxon[]} taxa
|
202
|
+
* @param {import("./taxon.js").Taxon[]} taxa
|
203
203
|
*/
|
204
204
|
constructor(outputDir, name, taxa) {
|
205
205
|
super(outputDir, name, name);
|
@@ -207,7 +207,7 @@ class PageSection extends GenericPage {
|
|
207
207
|
}
|
208
208
|
|
209
209
|
/**
|
210
|
-
* @param {
|
210
|
+
* @param {import("./htmltaxon.js").TaxaColDef[]} [columns]
|
211
211
|
*/
|
212
212
|
render(columns) {
|
213
213
|
let html = this.getDefaultIntro();
|
@@ -219,12 +219,12 @@ class PageSection extends GenericPage {
|
|
219
219
|
}
|
220
220
|
|
221
221
|
class Sections {
|
222
|
-
/** @type {Object<string,Taxon[]>} */
|
222
|
+
/** @type {Object<string,import("./taxon.js").Taxon[]>} */
|
223
223
|
static #sections = {};
|
224
224
|
|
225
225
|
/**
|
226
226
|
* @param {string} name
|
227
|
-
* @param {Taxon} taxon
|
227
|
+
* @param {import("./taxon.js").Taxon} taxon
|
228
228
|
*/
|
229
229
|
static addTaxon(name, taxon) {
|
230
230
|
let section = this.#sections[name];
|
package/lib/genera.js
CHANGED
package/lib/htmltaxon.js
CHANGED
@@ -6,7 +6,15 @@ import { RarePlants } from "./rareplants.js";
|
|
6
6
|
import { TextUtils } from "./textutils.js";
|
7
7
|
|
8
8
|
/**
|
9
|
-
* @
|
9
|
+
* @typedef {{
|
10
|
+
class?: string;
|
11
|
+
data: (taxon: import("./taxon.js").Taxon) => string;
|
12
|
+
title: string;
|
13
|
+
}} TaxaColDef
|
14
|
+
*/
|
15
|
+
|
16
|
+
/**
|
17
|
+
* @type {Record<string,TaxaColDef>}
|
10
18
|
*/
|
11
19
|
const TAXA_LIST_COLS = {
|
12
20
|
CESA: {
|
@@ -39,6 +47,7 @@ const TAXA_LIST_COLS = {
|
|
39
47
|
},
|
40
48
|
};
|
41
49
|
|
50
|
+
/** @type {TaxaColDef[]} */
|
42
51
|
const DEFAULT_TAXA_COLUMNS = [
|
43
52
|
TAXA_LIST_COLS.SPECIES,
|
44
53
|
TAXA_LIST_COLS.COMMON_NAME,
|
@@ -46,7 +55,7 @@ const DEFAULT_TAXA_COLUMNS = [
|
|
46
55
|
|
47
56
|
class HTMLTaxon {
|
48
57
|
/**
|
49
|
-
* @param {Taxon} taxon
|
58
|
+
* @param {import("./taxon.js").Taxon} taxon
|
50
59
|
* @returns {string|undefined}
|
51
60
|
*/
|
52
61
|
static getCalscapeLink(taxon) {
|
@@ -88,7 +97,7 @@ class HTMLTaxon {
|
|
88
97
|
}
|
89
98
|
|
90
99
|
/**
|
91
|
-
* @param {Taxon} taxon
|
100
|
+
* @param {import("./taxon.js").Taxon} taxon
|
92
101
|
* @param {string} classNames
|
93
102
|
* @param {boolean} [includeColorLink=true]
|
94
103
|
*/
|
@@ -129,7 +138,7 @@ class HTMLTaxon {
|
|
129
138
|
}
|
130
139
|
|
131
140
|
/**
|
132
|
-
* @param {Taxon} taxon
|
141
|
+
* @param {import("./taxon.js").Taxon} taxon
|
133
142
|
* @returns {string}
|
134
143
|
*/
|
135
144
|
static getFooterHTML(taxon) {
|
@@ -142,7 +151,7 @@ class HTMLTaxon {
|
|
142
151
|
}
|
143
152
|
|
144
153
|
/**
|
145
|
-
* @param {Taxon} taxon
|
154
|
+
* @param {import("./taxon.js").Taxon} taxon
|
146
155
|
*/
|
147
156
|
static getLink(taxon) {
|
148
157
|
return (
|
@@ -182,8 +191,8 @@ class HTMLTaxon {
|
|
182
191
|
}
|
183
192
|
|
184
193
|
/**
|
185
|
-
* @param {Taxon[]} taxa
|
186
|
-
* @param {
|
194
|
+
* @param {import("./taxon.js").Taxon[]} taxa
|
195
|
+
* @param {TaxaColDef[]} [columns]
|
187
196
|
*/
|
188
197
|
static getTaxaTable(taxa, columns = DEFAULT_TAXA_COLUMNS) {
|
189
198
|
let html = "<table><thead>";
|
package/lib/index.d.ts
CHANGED
@@ -79,6 +79,16 @@ export class Jekyll {
|
|
79
79
|
static include(fileName: string): string;
|
80
80
|
}
|
81
81
|
|
82
|
+
type PhotoRights = "CC0" | "CC BY" | "CC BY-NC" | "C" | null;
|
83
|
+
|
84
|
+
export class Photo {
|
85
|
+
getAttribution(): string;
|
86
|
+
getExt(): string;
|
87
|
+
getId(): number;
|
88
|
+
getSourceUrl(): string;
|
89
|
+
getUrl(): string;
|
90
|
+
}
|
91
|
+
|
82
92
|
export class Program {
|
83
93
|
static getIncludeList(dataDir: string): string[];
|
84
94
|
static getProgram(): Command;
|
@@ -101,14 +111,19 @@ export class Taxon {
|
|
101
111
|
getBaseFileName(): string;
|
102
112
|
getCalfloraID(): string;
|
103
113
|
getCalfloraTaxonLink(): string;
|
114
|
+
getCESA(): string | undefined;
|
115
|
+
getCNDDBRank(): string | undefined;
|
104
116
|
getCommonNames(): string[];
|
105
117
|
getFamily(): Family;
|
118
|
+
getFESA(): string | undefined;
|
106
119
|
getGenus(): Genus;
|
107
120
|
getGenusName(): string;
|
121
|
+
getGlobalRank(): string | undefined;
|
108
122
|
getINatID(): string;
|
109
123
|
getINatTaxonLink(): string;
|
110
124
|
getJepsonID(): string;
|
111
125
|
getName(): string;
|
126
|
+
getPhotos(): Photo[];
|
112
127
|
getRPIRankAndThreat(): string;
|
113
128
|
getRPITaxonLink(): string;
|
114
129
|
getSynonyms(): string[];
|
package/lib/pagerenderer.js
CHANGED
@@ -7,259 +7,262 @@ import { HTML } from "./html.js";
|
|
7
7
|
import { HTMLTaxon, TAXA_LIST_COLS } from "./htmltaxon.js";
|
8
8
|
|
9
9
|
const ENDANGERED_COLS = [
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
10
|
+
TAXA_LIST_COLS.SPECIES,
|
11
|
+
TAXA_LIST_COLS.COMMON_NAME,
|
12
|
+
TAXA_LIST_COLS.CESA,
|
13
|
+
TAXA_LIST_COLS.FESA,
|
14
14
|
];
|
15
15
|
const RPI_COLUMNS = [
|
16
|
-
|
17
|
-
|
18
|
-
|
16
|
+
TAXA_LIST_COLS.SPECIES_BARE,
|
17
|
+
TAXA_LIST_COLS.COMMON_NAME,
|
18
|
+
TAXA_LIST_COLS.CNPS_RANK,
|
19
19
|
];
|
20
20
|
|
21
21
|
class PageRenderer extends BasePageRenderer {
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
22
|
+
/**
|
23
|
+
* @param {string} outputDir
|
24
|
+
* @param {import('./config.js').Config} config
|
25
|
+
* @param {import("./taxa.js").Taxa} taxa
|
26
|
+
*/
|
27
|
+
static render(outputDir, config, taxa) {
|
28
|
+
super.renderBasePages(outputDir, taxa);
|
29
29
|
|
30
|
-
|
30
|
+
this.renderLists(outputDir, config, taxa);
|
31
31
|
|
32
|
-
|
33
|
-
|
34
|
-
|
32
|
+
const taxonList = taxa.getTaxonList();
|
33
|
+
for (const taxon of taxonList) {
|
34
|
+
new PageTaxon(outputDir, config, taxon).render();
|
35
|
+
}
|
35
36
|
}
|
36
|
-
}
|
37
37
|
|
38
|
-
/**
|
39
|
-
* @param {string} outputDir
|
40
|
-
* @param {Config} config
|
41
|
-
* @param {Taxa} taxa
|
42
|
-
*/
|
43
|
-
static renderLists(outputDir, config, taxa) {
|
44
38
|
/**
|
45
|
-
* @param {
|
46
|
-
* @param {
|
47
|
-
* @param {
|
48
|
-
* @returns {string}
|
39
|
+
* @param {string} outputDir
|
40
|
+
* @param {import('./config.js').Config} config
|
41
|
+
* @param {import("./taxa.js").Taxa} taxa
|
49
42
|
*/
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
43
|
+
static renderLists(outputDir, config, taxa) {
|
44
|
+
/**
|
45
|
+
* @param {ListInfo[]} listInfo
|
46
|
+
* @param {Object<string,string>} attributes
|
47
|
+
* @param {import("./htmltaxon.js").TaxaColDef[]} [columns]
|
48
|
+
* @returns {string}
|
49
|
+
*/
|
50
|
+
function getListArray(listInfo, attributes = {}, columns) {
|
51
|
+
const listArray = [];
|
52
|
+
for (const list of listInfo) {
|
53
|
+
const listTaxa = [];
|
54
|
+
const calfloraTaxa = [];
|
55
|
+
const iNatTaxa = [];
|
56
|
+
for (const taxon of taxa.getTaxonList()) {
|
57
|
+
if (list.include(taxon)) {
|
58
|
+
listTaxa.push(taxon);
|
59
|
+
calfloraTaxa.push(taxon.getCalfloraName());
|
60
|
+
iNatTaxa.push(taxon.getINatName());
|
61
|
+
}
|
62
|
+
}
|
63
63
|
|
64
|
-
|
65
|
-
|
66
|
-
|
64
|
+
if (listTaxa.length === 0) {
|
65
|
+
continue;
|
66
|
+
}
|
67
67
|
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
68
|
+
Files.write(
|
69
|
+
outputDir + "/calflora_" + list.filename + ".txt",
|
70
|
+
calfloraTaxa.join("\n"),
|
71
|
+
);
|
72
|
+
Files.write(
|
73
|
+
outputDir + "/inat_" + list.filename + ".txt",
|
74
|
+
iNatTaxa.join("\n"),
|
75
|
+
);
|
76
76
|
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
77
|
+
const cols = columns ? columns : list.columns;
|
78
|
+
new PageTaxonList(outputDir, list.name, list.filename).render(
|
79
|
+
listTaxa,
|
80
|
+
cols,
|
81
|
+
);
|
82
82
|
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
83
|
+
// Check for sublists.
|
84
|
+
const subListHTML = list.listInfo
|
85
|
+
? getListArray(list.listInfo, { class: "indent" }, cols)
|
86
|
+
: "";
|
87
87
|
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
88
|
+
listArray.push(
|
89
|
+
HTML.getLink("./" + list.filename + ".html", list.name) +
|
90
|
+
" (" +
|
91
|
+
listTaxa.length +
|
92
|
+
")" +
|
93
|
+
subListHTML,
|
94
|
+
);
|
95
|
+
}
|
96
96
|
|
97
|
-
|
98
|
-
|
97
|
+
return renderList(listArray, attributes);
|
98
|
+
}
|
99
99
|
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
100
|
+
/**
|
101
|
+
* @param {string[]} listsHTML
|
102
|
+
* @param {Object<string,string>} attributes
|
103
|
+
*/
|
104
|
+
function renderList(listsHTML, attributes = {}) {
|
105
|
+
return HTML.wrap("ul", HTML.arrayToLI(listsHTML), attributes);
|
106
|
+
}
|
107
107
|
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
108
|
+
/**
|
109
|
+
* @param {string} title
|
110
|
+
* @param {string} listsHTML
|
111
|
+
*/
|
112
|
+
function renderSection(title, listsHTML) {
|
113
|
+
let html = '<div class="section nobullet">';
|
114
|
+
html += HTML.textElement("h2", title);
|
115
|
+
html += listsHTML;
|
116
|
+
html += "</div>";
|
117
|
+
return html;
|
118
|
+
}
|
119
119
|
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
120
|
+
/** @typedef {{name:string,filename:string,include:function(import("./taxon.js").Taxon):boolean,columns?:import("./htmltaxon.js").TaxaColDef[],listInfo?:ListInfo[]}} ListInfo */
|
121
|
+
/** @type {{title:string,listInfo:ListInfo[]}[]} */
|
122
|
+
const sections = [
|
123
|
+
{
|
124
|
+
title: "All Species",
|
125
|
+
listInfo: [
|
126
|
+
{
|
127
|
+
name: config.getLabel("native", "Native"),
|
128
|
+
filename: "list_native",
|
129
|
+
include: (t) => t.isNative(),
|
130
|
+
},
|
131
|
+
{
|
132
|
+
name: config.getLabel("introduced", "Introduced"),
|
133
|
+
filename: "list_introduced",
|
134
|
+
include: (t) => !t.isNative(),
|
135
|
+
},
|
136
|
+
{
|
137
|
+
name: "All Plants",
|
138
|
+
filename: "list_all",
|
139
|
+
include: () => true,
|
140
|
+
},
|
141
|
+
],
|
142
|
+
},
|
143
|
+
{
|
144
|
+
title: "Rare Plants",
|
145
|
+
listInfo: [
|
146
|
+
{
|
147
|
+
name: "CNPS Ranked Plants",
|
148
|
+
filename: "list_rpi",
|
149
|
+
include: (t) => t.getRPIRank() !== undefined,
|
150
|
+
columns: RPI_COLUMNS,
|
151
|
+
listInfo: [
|
152
|
+
{
|
153
|
+
name: RarePlants.getRPIRankDescription("1A"),
|
154
|
+
filename: "list_rpi_1a",
|
155
|
+
include: (t) => t.getRPIRank() === "1A",
|
156
|
+
},
|
157
|
+
{
|
158
|
+
name: RarePlants.getRPIRankDescription("1B"),
|
159
|
+
filename: "list_rpi_1b",
|
160
|
+
include: (t) => t.getRPIRank() === "1B",
|
161
|
+
},
|
162
|
+
{
|
163
|
+
name: RarePlants.getRPIRankDescription("2A"),
|
164
|
+
filename: "list_rpi_2a",
|
165
|
+
include: (t) => t.getRPIRank() === "2A",
|
166
|
+
},
|
167
|
+
{
|
168
|
+
name: RarePlants.getRPIRankDescription("2B"),
|
169
|
+
filename: "list_rpi_2b",
|
170
|
+
include: (t) => t.getRPIRank() === "2B",
|
171
|
+
},
|
172
|
+
{
|
173
|
+
name: RarePlants.getRPIRankDescription("3"),
|
174
|
+
filename: "list_rpi_3",
|
175
|
+
include: (t) => t.getRPIRank() === "3",
|
176
|
+
},
|
177
|
+
{
|
178
|
+
name: RarePlants.getRPIRankDescription("4"),
|
179
|
+
filename: "list_rpi_4",
|
180
|
+
include: (t) => t.getRPIRank() === "4",
|
181
|
+
},
|
182
|
+
],
|
183
|
+
},
|
184
|
+
{
|
185
|
+
name: "Endangered Species",
|
186
|
+
filename: "list_endangered",
|
187
|
+
include: (t) =>
|
188
|
+
t.getCESA() !== undefined ||
|
189
|
+
t.getFESA() !== undefined,
|
190
|
+
columns: ENDANGERED_COLS,
|
191
|
+
},
|
192
|
+
],
|
193
|
+
},
|
194
|
+
];
|
194
195
|
|
195
|
-
|
196
|
-
|
197
|
-
|
196
|
+
let html = '<div class="wrapper">';
|
197
|
+
for (const section of sections) {
|
198
|
+
const listHTML = getListArray(section.listInfo);
|
198
199
|
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
200
|
+
if (listHTML.length > 0) {
|
201
|
+
html += renderSection(section.title, listHTML);
|
202
|
+
}
|
203
|
+
}
|
204
|
+
html += renderSection(
|
205
|
+
"Taxonomy",
|
206
|
+
renderList([
|
207
|
+
HTML.getLink("./list_families.html", "Plant Families"),
|
208
|
+
]),
|
209
|
+
);
|
207
210
|
|
208
|
-
|
211
|
+
html += "</div>";
|
209
212
|
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
+
// Write lists to includes directory so it can be inserted into pages.
|
214
|
+
Files.write(outputDir + "/_includes/plantlists.html", html);
|
215
|
+
}
|
213
216
|
}
|
214
217
|
|
215
218
|
class PageTaxonList extends GenericPage {
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
219
|
+
/**
|
220
|
+
* @param {string} outputDir
|
221
|
+
* @param {string} title
|
222
|
+
* @param {string} baseName
|
223
|
+
*/
|
224
|
+
constructor(outputDir, title, baseName) {
|
225
|
+
super(outputDir, title, baseName);
|
226
|
+
}
|
224
227
|
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
228
|
+
/**
|
229
|
+
*
|
230
|
+
* @param {import("./taxon.js").Taxon[]} taxa
|
231
|
+
* @param {import("./htmltaxon.js").TaxaColDef[]|undefined} columns
|
232
|
+
*/
|
233
|
+
render(taxa, columns) {
|
234
|
+
let html = this.getDefaultIntro();
|
232
235
|
|
233
|
-
|
236
|
+
html += '<div class="wrapper">';
|
234
237
|
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
+
html += '<div class="section">';
|
239
|
+
html += HTMLTaxon.getTaxaTable(taxa, columns);
|
240
|
+
html += "</div>";
|
238
241
|
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
242
|
+
html += '<div class="section nobullet">';
|
243
|
+
html += HTML.textElement("h2", "Download");
|
244
|
+
html += "<ul>";
|
245
|
+
html +=
|
246
|
+
"<li>" +
|
247
|
+
HTML.getLink(
|
248
|
+
"./calflora_" + this.getBaseFileName() + ".txt",
|
249
|
+
"Calflora List",
|
250
|
+
) +
|
251
|
+
"</li>";
|
252
|
+
html +=
|
253
|
+
"<li>" +
|
254
|
+
HTML.getLink(
|
255
|
+
"./inat_" + this.getBaseFileName() + ".txt",
|
256
|
+
"iNaturalist List",
|
257
|
+
) +
|
258
|
+
"</li>";
|
259
|
+
html += "</ul>";
|
260
|
+
html += "</div>";
|
258
261
|
|
259
|
-
|
262
|
+
html += "</div>";
|
260
263
|
|
261
|
-
|
262
|
-
|
264
|
+
this.writeFile(html);
|
265
|
+
}
|
263
266
|
}
|
264
267
|
|
265
268
|
export { PageRenderer };
|
package/lib/photo.js
CHANGED
@@ -1,44 +1,74 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
1
|
+
/**
|
2
|
+
* @typedef {"CC0" | "CC BY" | "CC BY-NC" | "C" | null} PhotoRights
|
3
|
+
* @typedef { "cc-by-nc-sa"
|
4
|
+
| "cc-by-nc"
|
5
|
+
| "cc-by-nc-nd"
|
6
|
+
| "cc-by"
|
7
|
+
| "cc-by-sa"
|
8
|
+
| "cc-by-nd"
|
9
|
+
| "pd"
|
10
|
+
| "gdfl"
|
11
|
+
| "cc0"} InatLicenseCode
|
12
|
+
*/
|
5
13
|
|
6
|
-
class Photo {
|
7
|
-
|
8
|
-
#
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
rights;
|
14
|
+
export class Photo {
|
15
|
+
#id;
|
16
|
+
#ext;
|
17
|
+
#rightsHolder;
|
18
|
+
/** @type {PhotoRights} */
|
19
|
+
#rights;
|
13
20
|
|
14
21
|
/**
|
15
|
-
* @param {
|
16
|
-
* @param {string
|
17
|
-
* @param {
|
22
|
+
* @param {number} id
|
23
|
+
* @param {string} ext
|
24
|
+
* @param {InatLicenseCode} licenseCode
|
25
|
+
* @param {string} rightsHolder
|
18
26
|
*/
|
19
|
-
constructor(
|
20
|
-
this.#
|
21
|
-
this
|
22
|
-
this
|
27
|
+
constructor(id, ext, licenseCode, rightsHolder) {
|
28
|
+
this.#id = id;
|
29
|
+
this.#ext = ext;
|
30
|
+
this.#rightsHolder = rightsHolder;
|
31
|
+
if (licenseCode === "cc0") this.#rights = "CC0";
|
32
|
+
else if (licenseCode === "cc-by") this.#rights = "CC BY";
|
33
|
+
else if (licenseCode === "cc-by-nc") this.#rights = "CC BY-NC";
|
34
|
+
else this.#rights = "C";
|
23
35
|
}
|
24
36
|
|
25
|
-
|
26
|
-
|
37
|
+
/**
|
38
|
+
* @returns {string}
|
39
|
+
*/
|
40
|
+
getAttribution() {
|
41
|
+
if (this.#rights === "CC0") {
|
42
|
+
if (this.#rightsHolder) {
|
43
|
+
return `By ${this.#rightsHolder} (${this.#rights})`;
|
44
|
+
}
|
45
|
+
return this.#rights;
|
46
|
+
}
|
47
|
+
if (this.#rightsHolder) {
|
48
|
+
return `(c) ${this.#rightsHolder} (${this.#rights})`;
|
49
|
+
}
|
50
|
+
return `(c) (${this.#rights})`;
|
51
|
+
}
|
52
|
+
|
53
|
+
getExt() {
|
54
|
+
return this.#ext;
|
55
|
+
}
|
56
|
+
|
57
|
+
getId() {
|
58
|
+
return this.#id;
|
27
59
|
}
|
28
60
|
|
29
61
|
/**
|
30
|
-
*
|
31
|
-
* @return {string?}
|
62
|
+
* @returns {string} The URL of the iNaturalist page with details about the image.
|
32
63
|
*/
|
33
64
|
getSourceUrl() {
|
34
|
-
return
|
65
|
+
return `https://www.inaturalist.org/photos/${this.#id}`;
|
35
66
|
}
|
36
|
-
}
|
37
67
|
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
}
|
68
|
+
/**
|
69
|
+
* @returns {string} The URL to retrieve the image file.
|
70
|
+
*/
|
71
|
+
getUrl() {
|
72
|
+
return `https://inaturalist-open-data.s3.amazonaws.com/photos/${this.#id}/medium.${this.#ext}`;
|
73
|
+
}
|
74
|
+
}
|
package/lib/taxa.js
CHANGED
@@ -7,10 +7,10 @@ import { Genera } from "./genera.js";
|
|
7
7
|
import { Taxon } from "./taxon.js";
|
8
8
|
import { Families } from "./families.js";
|
9
9
|
import { FlowerColor } from "./flowercolor.js";
|
10
|
-
import { InatPhoto } from "./inat_photo.js";
|
11
10
|
import { TaxaCSV } from "./tools/taxacsv.js";
|
12
11
|
import { ErrorLog } from "./errorlog.js";
|
13
12
|
import { Program } from "./program.js";
|
13
|
+
import { Photo } from "./photo.js";
|
14
14
|
|
15
15
|
const FLOWER_COLORS = [
|
16
16
|
{ name: "white", color: "white" },
|
@@ -138,7 +138,7 @@ class Taxa {
|
|
138
138
|
continue;
|
139
139
|
}
|
140
140
|
taxon.addPhoto(
|
141
|
-
new
|
141
|
+
new Photo(
|
142
142
|
csvPhoto.id,
|
143
143
|
csvPhoto.ext,
|
144
144
|
csvPhoto.licenseCode,
|
package/lib/taxon.js
CHANGED
@@ -35,12 +35,12 @@ class Taxon {
|
|
35
35
|
#rankGlobal;
|
36
36
|
/** @type {string[]} */
|
37
37
|
#synonyms = [];
|
38
|
-
/** @type {Photo[]}
|
38
|
+
/** @type {import("./photo.js").Photo[]}*/
|
39
39
|
#photos = [];
|
40
40
|
|
41
41
|
/**
|
42
42
|
* @param {TaxonData} data
|
43
|
-
* @param {Genera} genera
|
43
|
+
* @param {import("./genera.js").Genera} genera
|
44
44
|
*/
|
45
45
|
constructor(data, genera) {
|
46
46
|
this.#genera = genera;
|
@@ -98,12 +98,15 @@ class Taxon {
|
|
98
98
|
}
|
99
99
|
|
100
100
|
/**
|
101
|
-
* @param {
|
101
|
+
* @param {import("./photo.js").Photo} photo
|
102
102
|
*/
|
103
103
|
addPhoto(photo) {
|
104
104
|
this.#photos = this.#photos.concat([photo]);
|
105
105
|
}
|
106
106
|
|
107
|
+
/**
|
108
|
+
* @returns {import("./photo.js").Photo[]}
|
109
|
+
*/
|
107
110
|
getPhotos() {
|
108
111
|
return this.#photos;
|
109
112
|
}
|
package/lib/tools/rpi.js
CHANGED
@@ -12,7 +12,7 @@ class RPI {
|
|
12
12
|
|
13
13
|
/**
|
14
14
|
* @param {string} toolsDataDir
|
15
|
-
* @param {Taxa} taxa
|
15
|
+
* @param {import("../taxa.js").Taxa} taxa
|
16
16
|
* @param {Config} config
|
17
17
|
* @param {import("../exceptions.js").Exceptions} exceptions
|
18
18
|
* @param {ErrorLog} errorLog
|
@@ -202,8 +202,7 @@ class RPI {
|
|
202
202
|
}
|
203
203
|
|
204
204
|
/**
|
205
|
-
*
|
206
|
-
* @param {Taxa} taxa
|
205
|
+
* @param {import("../taxa.js").Taxa} taxa
|
207
206
|
* @param {Config} config
|
208
207
|
* @param {import("../exceptions.js").Exceptions} exceptions
|
209
208
|
* @param {ErrorLog} errorLog
|
@@ -367,7 +366,7 @@ class RPI {
|
|
367
366
|
|
368
367
|
/**
|
369
368
|
* @param {string} toolsDataDir
|
370
|
-
* @param {Taxa} taxa
|
369
|
+
* @param {import("../taxa.js").Taxa} taxa
|
371
370
|
* @param {import("../exceptions.js").Exceptions} exceptions
|
372
371
|
* @param {ErrorLog} errorLog
|
373
372
|
*/
|
package/lib/web/pagetaxon.js
CHANGED
@@ -11,8 +11,8 @@ class PageTaxon extends GenericPage {
|
|
11
11
|
|
12
12
|
/**
|
13
13
|
* @param {string} outputDir
|
14
|
-
* @param {Config} config
|
15
|
-
* @param {Taxon} taxon
|
14
|
+
* @param {import("../config.js").Config} config
|
15
|
+
* @param {import("../taxon.js").Taxon} taxon
|
16
16
|
*/
|
17
17
|
constructor(outputDir, config, taxon) {
|
18
18
|
super(outputDir, taxon.getName(), taxon.getBaseFileName());
|
@@ -189,7 +189,7 @@ class PageTaxon extends GenericPage {
|
|
189
189
|
|
190
190
|
html += HTMLTaxon.getFooterHTML(this.#taxon);
|
191
191
|
|
192
|
-
const photos = this.#taxon.getPhotos().slice(
|
192
|
+
const photos = this.#taxon.getPhotos().slice(0, 5);
|
193
193
|
if (photos.length > 0) {
|
194
194
|
let photosHtml = "";
|
195
195
|
for (const photo of photos) {
|
@@ -202,9 +202,7 @@ class PageTaxon extends GenericPage {
|
|
202
202
|
/>
|
203
203
|
</a>
|
204
204
|
<figcaption>
|
205
|
-
${photo.
|
206
|
-
${photo.rightsHolder}
|
207
|
-
${photo.rights && `(${photo.rights})`}
|
205
|
+
${photo.getAttribution()}
|
208
206
|
</figcaption>
|
209
207
|
</figure>
|
210
208
|
`;
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "@ca-plant-list/ca-plant-list",
|
3
|
-
"version": "0.4.
|
3
|
+
"version": "0.4.19",
|
4
4
|
"description": "Tools to create Jekyll files for a website listing plants in an area of California.",
|
5
5
|
"license": "MIT",
|
6
6
|
"repository": {
|
@@ -22,8 +22,9 @@
|
|
22
22
|
},
|
23
23
|
"types": "./lib/index.d.ts",
|
24
24
|
"scripts": {
|
25
|
-
"check": "npm run eslint && npm run tsc",
|
25
|
+
"check": "npm run eslint && npm run tsc && npm run jest",
|
26
26
|
"eslint": "npx eslint",
|
27
|
+
"jest": "node --experimental-vm-modules node_modules/jest/bin/jest.js tests",
|
27
28
|
"prettier": "npx prettier -l .",
|
28
29
|
"tsc": "npx tsc"
|
29
30
|
},
|
@@ -51,10 +52,12 @@
|
|
51
52
|
"devDependencies": {
|
52
53
|
"@types/archiver": "^6.0.2",
|
53
54
|
"@types/cli-progress": "^3.11.6",
|
55
|
+
"@types/jest": "^29.5.14",
|
54
56
|
"@types/markdown-it": "^14.1.2",
|
55
57
|
"@types/node": "^22.10.3",
|
56
58
|
"@types/unzipper": "^0.10.9",
|
57
59
|
"eslint": "^9.17.0",
|
60
|
+
"jest": "^29.7.0",
|
58
61
|
"prettier": "^3.4.2",
|
59
62
|
"typescript": "^5.7.2"
|
60
63
|
}
|
package/types/classes.d.ts
CHANGED
@@ -23,7 +23,6 @@ declare class ErrorLog {
|
|
23
23
|
declare class Families {
|
24
24
|
getFamilies(): Family[];
|
25
25
|
getFamily(name: string): Family;
|
26
|
-
renderPages(outputDir: string, cols?: TaxaCol[]): void;
|
27
26
|
}
|
28
27
|
|
29
28
|
declare class Family {
|
@@ -41,7 +40,6 @@ declare class FlowerColor {
|
|
41
40
|
}
|
42
41
|
|
43
42
|
declare class Genera {
|
44
|
-
addTaxon(taxon: Taxon): void;
|
45
43
|
getGenus(name: string): Genus;
|
46
44
|
}
|
47
45
|
|
@@ -87,15 +85,8 @@ declare class Taxa {
|
|
87
85
|
isSubset(): boolean;
|
88
86
|
}
|
89
87
|
|
90
|
-
declare class TaxaCol {
|
91
|
-
class?: string;
|
92
|
-
data: (taxon: Taxon) => string;
|
93
|
-
title: string;
|
94
|
-
}
|
95
|
-
|
96
88
|
type StatusCode = "N" | "NC" | "U" | "X";
|
97
89
|
declare class Taxon {
|
98
|
-
constructor(data: TaxonData, genera: Genera, meta: any);
|
99
90
|
getBaseFileName(): string;
|
100
91
|
getBloomEnd(): number | undefined;
|
101
92
|
getBloomStart(): number | undefined;
|
@@ -104,8 +95,6 @@ declare class Taxon {
|
|
104
95
|
getCalfloraTaxonLink(): string | undefined;
|
105
96
|
getCalscapeCommonName(): string | undefined;
|
106
97
|
getCalscapeName(): string;
|
107
|
-
getCESA(): string | undefined;
|
108
|
-
getCNDDBRank(): string | undefined;
|
109
98
|
getCommonNames(): string[];
|
110
99
|
getFamily(): Family;
|
111
100
|
getFESA(): string | undefined;
|
@@ -125,7 +114,6 @@ declare class Taxon {
|
|
125
114
|
getJepsonID(): string;
|
126
115
|
getLifeCycle(): string;
|
127
116
|
getName(): string;
|
128
|
-
getPhotos(): Photo[];
|
129
117
|
getRPIID(): string | undefined;
|
130
118
|
getRPIRank(): string;
|
131
119
|
getRPIRankAndThreat(): string;
|
@@ -160,21 +148,6 @@ declare class TaxonData {
|
|
160
148
|
|
161
149
|
type PhotoRights = "CC0" | "CC BY" | "CC BY-NC" | "C" | null;
|
162
150
|
|
163
|
-
declare class Photo {
|
164
|
-
url?: string;
|
165
|
-
rightsHolder: null | string;
|
166
|
-
rights?: PhotoRights;
|
167
|
-
getExt(): string;
|
168
|
-
getId(): number;
|
169
|
-
getUrl(): string;
|
170
|
-
getSourceUrl(): string;
|
171
|
-
}
|
172
|
-
|
173
|
-
declare class InatPhoto extends Photo {
|
174
|
-
inatPhotoId: number;
|
175
|
-
ext: string;
|
176
|
-
}
|
177
|
-
|
178
151
|
type InatPhotoInfo = {
|
179
152
|
id: string;
|
180
153
|
ext: string;
|
@@ -219,7 +192,7 @@ declare class InatApiTaxon {
|
|
219
192
|
declare class InatApiObservation {
|
220
193
|
observation_photos: {
|
221
194
|
photo: InatApiPhoto;
|
222
|
-
}[]
|
195
|
+
}[];
|
223
196
|
}
|
224
197
|
|
225
198
|
declare class InatObsPhotosCommandLineOptions extends CommandLineOptions {
|
package/lib/inat_photo.js
DELETED
@@ -1,43 +0,0 @@
|
|
1
|
-
import { CC0, CC_BY, CC_BY_NC, COPYRIGHT, Photo } from "./photo.js";
|
2
|
-
|
3
|
-
class InatPhoto extends Photo {
|
4
|
-
/** @type {number} */
|
5
|
-
inatPhotoId;
|
6
|
-
/** @type {string} */
|
7
|
-
ext;
|
8
|
-
|
9
|
-
/**
|
10
|
-
* @param {number} id
|
11
|
-
* @param {string} ext
|
12
|
-
* @param {InatLicenseCode} licenseCode
|
13
|
-
* @param {string} attrName
|
14
|
-
*/
|
15
|
-
constructor(id, ext, licenseCode, attrName) {
|
16
|
-
/** @type {typeof COPYRIGHT | typeof CC_BY | typeof CC_BY_NC | typeof CC0} */
|
17
|
-
let rights = COPYRIGHT;
|
18
|
-
if (licenseCode === "cc0") rights = CC0;
|
19
|
-
else if (licenseCode === "cc-by") rights = CC_BY;
|
20
|
-
else if (licenseCode === "cc-by-nc") rights = CC_BY_NC;
|
21
|
-
super(null, attrName, rights);
|
22
|
-
this.inatPhotoId = id;
|
23
|
-
this.ext = ext;
|
24
|
-
}
|
25
|
-
|
26
|
-
getExt() {
|
27
|
-
return this.ext;
|
28
|
-
}
|
29
|
-
|
30
|
-
getId() {
|
31
|
-
return this.inatPhotoId;
|
32
|
-
}
|
33
|
-
|
34
|
-
getUrl() {
|
35
|
-
return `https://inaturalist-open-data.s3.amazonaws.com/photos/${this.inatPhotoId}/medium.${this.ext}`;
|
36
|
-
}
|
37
|
-
|
38
|
-
getSourceUrl() {
|
39
|
-
return `https://www.inaturalist.org/photos/${this.inatPhotoId}`;
|
40
|
-
}
|
41
|
-
}
|
42
|
-
|
43
|
-
export { InatPhoto };
|