@ca-plant-list/ca-plant-list 0.3.2 → 0.3.4
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/.vscode/settings.json +4 -2
- package/data/exceptions.json +0 -3
- package/data/glossary/calyx.md +1 -0
- package/data/glossary/ovary.md +3 -0
- package/data/glossary/pedicel.md +1 -0
- package/data/glossary/pistil.md +3 -0
- package/data/glossary/sepal.md +1 -0
- package/data/glossary/stigma.md +3 -0
- package/data/glossary/style.md +3 -0
- package/data/illustrations/inkscape/pistil.svg +156 -0
- package/data/synonyms.csv +8 -3
- package/data/taxa.csv +77 -72
- package/data/text/Calochortus-argillosus.md +1 -0
- package/data/text/Calochortus-luteus.md +1 -0
- package/data/text/Calochortus-venustus.md +1 -0
- package/data/text/Ceanothus-cuneatus-var-cuneatus.md +1 -0
- package/data/text/Ceanothus-leucodermis.md +1 -0
- package/data/text/Claytonia-parviflora.md +1 -0
- package/data/text/Claytonia-perfoliata.md +1 -0
- package/data/text/Collinsia-heterophylla-var-heterophylla.md +1 -0
- package/data/text/Delphinium-californicum-subsp-californicum.md +1 -0
- package/data/text/Delphinium-decorum-subsp-decorum.md +1 -0
- package/data/text/Delphinium-hesperium-subsp-hesperium.md +1 -0
- package/data/text/Delphinium-patens-subsp-patens.md +1 -0
- package/data/text/Galium-andrewsii-subsp-gatense.md +1 -0
- package/data/text/Helianthella-californica-var-californica.md +1 -0
- package/data/text/Helianthella-castanea.md +1 -0
- package/data/text/Iris-macrosiphon.md +1 -0
- package/data/text/Lasthenia-californica-subsp-californica.md +1 -0
- package/data/text/Lasthenia-gracilis.md +1 -0
- package/data/text/Leptosiphon-ambiguus.md +1 -0
- package/data/text/Leptosiphon-androsaceus.md +1 -0
- package/data/text/Leptosiphon-bicolor.md +1 -1
- package/data/text/Leptosiphon-parviflorus.md +1 -0
- package/data/text/Lithophragma-affine.md +1 -1
- package/data/text/Lithophragma-parviflorum-var-parviflorum.md +1 -1
- package/data/text/Lomatium-californicum.md +0 -0
- package/data/text/Lomatium-dasycarpum-subsp-dasycarpum.md +1 -0
- package/data/text/Lomatium-utriculatum.md +1 -0
- package/data/text/Nemophila-heterophylla.md +1 -0
- package/data/text/Nemophila-parviflora-var-parviflora.md +1 -0
- package/data/text/Plectritis-ciliosa.md +1 -0
- package/data/text/Plectritis-macrocera.md +1 -0
- package/data/text/Primula-clevelandii-var-patula.md +1 -0
- package/data/text/Primula-hendersonii.md +1 -0
- package/data/text/Sidalcea-diploscypha.md +1 -0
- package/data/text/Thysanocarpus-curvipes.md +1 -0
- package/data/text/Thysanocarpus-laciniatus.md +1 -0
- package/data/text/Viola-douglasii.md +1 -0
- package/data/text/Viola-pedunculata.md +1 -0
- package/data/text/Viola-purpurea-subsp-quercetorum.md +1 -0
- package/ebook/css/main.css +5 -0
- package/lib/basepagerenderer.js +30 -29
- package/lib/dateutils.js +36 -16
- package/lib/ebook/pages/page_list_families.js +15 -9
- package/lib/ebook/pages/taxonpage.js +63 -36
- package/lib/ebook/plantbook.js +82 -48
- package/lib/errorlog.js +16 -11
- package/lib/families.js +109 -74
- package/lib/files.js +103 -45
- package/lib/genera.js +40 -26
- package/lib/generictaxaloader.js +15 -7
- package/lib/index.js +38 -3
- package/lib/taxa.js +174 -87
- package/lib/taxaloader.js +28 -15
- package/lib/taxaprocessor.js +6 -8
- package/lib/taxon.js +115 -57
- package/package.json +4 -6
- package/scripts/build-ebook.js +33 -25
- package/lib/index.d.ts +0 -345
package/lib/taxon.js
CHANGED
@@ -1,6 +1,7 @@
|
|
1
|
-
import {
|
2
|
-
import { HTML } from "./html.js";
|
1
|
+
import { HTML } from "./index.js";
|
3
2
|
import { RarePlants } from "./rareplants.js";
|
3
|
+
// eslint-disable-next-line no-unused-vars
|
4
|
+
import { Genera } from "./genera.js";
|
4
5
|
|
5
6
|
const TAXA_COLNAMES = {
|
6
7
|
BLOOM_START: "bloom_start",
|
@@ -10,15 +11,18 @@ const TAXA_COLNAMES = {
|
|
10
11
|
};
|
11
12
|
|
12
13
|
class Taxon {
|
13
|
-
|
14
|
+
/** @type {Genera} */
|
15
|
+
#genera;
|
14
16
|
#name;
|
15
17
|
#genus;
|
16
18
|
#commonNames;
|
17
19
|
#status;
|
18
20
|
#jepsonID;
|
19
21
|
#calRecNum;
|
22
|
+
/**@type {string|undefined} */
|
20
23
|
#cfSyn;
|
21
24
|
#iNatID;
|
25
|
+
/**@type {string|undefined} */
|
22
26
|
#iNatSyn;
|
23
27
|
#flowerColors;
|
24
28
|
#bloomStart;
|
@@ -29,38 +33,54 @@ class Taxon {
|
|
29
33
|
#fesa;
|
30
34
|
#rankCNDDB;
|
31
35
|
#rankGlobal;
|
36
|
+
/** @type {string[]} */
|
32
37
|
#synonyms = [];
|
33
38
|
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
const
|
39
|
+
/**
|
40
|
+
* @param {import("./index.js").TaxonData} data
|
41
|
+
* @param {Genera} genera
|
42
|
+
*/
|
43
|
+
constructor(data, genera) {
|
44
|
+
this.#genera = genera;
|
45
|
+
const name = data["taxon_name"];
|
46
|
+
const commonNames = data["common name"];
|
47
|
+
const cesa = data["CESA"];
|
48
|
+
const fesa = data["FESA"];
|
49
|
+
const rankGlobal = data["GRank"];
|
50
|
+
const rankCNDDB = data["SRank"];
|
41
51
|
this.#name = name;
|
42
|
-
this.#genus = name.split(
|
43
|
-
this.#commonNames = commonNames
|
44
|
-
|
45
|
-
|
46
|
-
this.#
|
47
|
-
this.#
|
48
|
-
|
49
|
-
this.#
|
50
|
-
|
51
|
-
this.#
|
52
|
-
|
53
|
-
|
52
|
+
this.#genus = name.split(" ")[0];
|
53
|
+
this.#commonNames = commonNames
|
54
|
+
? commonNames.split(",").map((t) => t.trim())
|
55
|
+
: [];
|
56
|
+
this.#status = data["status"];
|
57
|
+
this.#jepsonID = data["jepson id"];
|
58
|
+
this.#calRecNum = data["calrecnum"];
|
59
|
+
this.#iNatID = data["inat id"];
|
60
|
+
const colors = data["flower_color"];
|
61
|
+
this.#flowerColors = colors ? colors.split(",") : undefined;
|
62
|
+
if (data["bloom_start"]) {
|
63
|
+
this.#bloomStart = parseInt(data["bloom_start"]);
|
64
|
+
}
|
65
|
+
if (data["bloom_end"]) {
|
66
|
+
this.#bloomEnd = parseInt(data["bloom_end"]);
|
67
|
+
}
|
68
|
+
this.#rpiID = data["RPI ID"];
|
69
|
+
this.#rankRPI = data["CRPR"];
|
54
70
|
this.#cesa = cesa ? cesa : undefined;
|
55
71
|
this.#fesa = fesa ? fesa : undefined;
|
56
72
|
this.#rankCNDDB = rankCNDDB ? rankCNDDB : undefined;
|
57
73
|
this.#rankGlobal = rankGlobal ? rankGlobal : undefined;
|
58
|
-
|
74
|
+
genera.addTaxon(this);
|
59
75
|
}
|
60
76
|
|
61
|
-
|
62
|
-
|
63
|
-
|
77
|
+
/**
|
78
|
+
* @param {string} syn
|
79
|
+
* @param {string} type
|
80
|
+
*/
|
81
|
+
addSynonym(syn, type) {
|
82
|
+
this.#synonyms.push(syn);
|
83
|
+
switch (type) {
|
64
84
|
case "CF":
|
65
85
|
// Synonym is in Calflora format.
|
66
86
|
this.#cfSyn = syn;
|
@@ -74,22 +94,28 @@ class Taxon {
|
|
74
94
|
|
75
95
|
getBaseFileName() {
|
76
96
|
// Convert spaces to "-" and remove ".".
|
77
|
-
return this.#name.replaceAll(
|
97
|
+
return this.#name.replaceAll(" ", "-").replaceAll(".", "");
|
78
98
|
}
|
79
99
|
|
100
|
+
/**
|
101
|
+
* @returns {number|undefined}
|
102
|
+
*/
|
80
103
|
getBloomEnd() {
|
81
104
|
return this.#bloomEnd;
|
82
105
|
}
|
83
106
|
|
107
|
+
/**
|
108
|
+
* @returns {number|undefined}
|
109
|
+
*/
|
84
110
|
getBloomStart() {
|
85
111
|
return this.#bloomStart;
|
86
112
|
}
|
87
113
|
|
88
114
|
getCalfloraName() {
|
89
|
-
if (
|
115
|
+
if (this.#cfSyn) {
|
90
116
|
return this.#cfSyn;
|
91
117
|
}
|
92
|
-
return this.getName().replace(
|
118
|
+
return this.getName().replace(" subsp.", " ssp.").replace("×", "X");
|
93
119
|
}
|
94
120
|
|
95
121
|
getCalfloraID() {
|
@@ -98,11 +124,16 @@ class Taxon {
|
|
98
124
|
|
99
125
|
getCalfloraTaxonLink() {
|
100
126
|
const calfloraID = this.getCalfloraID();
|
101
|
-
if (
|
127
|
+
if (!calfloraID) {
|
102
128
|
return;
|
103
129
|
}
|
104
|
-
const link = HTML.getLink(
|
105
|
-
|
130
|
+
const link = HTML.getLink(
|
131
|
+
"https://www.calflora.org/app/taxon?crn=" + calfloraID,
|
132
|
+
"Calflora",
|
133
|
+
{},
|
134
|
+
true
|
135
|
+
);
|
136
|
+
return this.#cfSyn ? link + " (" + this.#cfSyn + ")" : link;
|
106
137
|
}
|
107
138
|
|
108
139
|
getCESA() {
|
@@ -118,14 +149,14 @@ class Taxon {
|
|
118
149
|
}
|
119
150
|
|
120
151
|
getFamily() {
|
121
|
-
return
|
152
|
+
return this.#genera.getFamily(this.#genus);
|
122
153
|
}
|
123
154
|
|
124
155
|
getFESA() {
|
125
156
|
return this.#fesa;
|
126
157
|
}
|
127
158
|
|
128
|
-
getFileName(
|
159
|
+
getFileName(ext = "html") {
|
129
160
|
return this.getBaseFileName() + "." + ext;
|
130
161
|
}
|
131
162
|
|
@@ -134,7 +165,7 @@ class Taxon {
|
|
134
165
|
}
|
135
166
|
|
136
167
|
getGenus() {
|
137
|
-
return
|
168
|
+
return this.#genera.getGenus(this.#genus);
|
138
169
|
}
|
139
170
|
|
140
171
|
getGenusName() {
|
@@ -145,18 +176,29 @@ class Taxon {
|
|
145
176
|
return this.#rankGlobal;
|
146
177
|
}
|
147
178
|
|
148
|
-
|
149
|
-
|
179
|
+
/**
|
180
|
+
*
|
181
|
+
* @param {boolean|string|undefined} href
|
182
|
+
* @param {boolean} includeRPI
|
183
|
+
*/
|
184
|
+
getHTMLLink(href = true, includeRPI = true) {
|
185
|
+
href = href ? "./" + this.getFileName() : undefined;
|
150
186
|
let className = this.isNative() ? "native" : "non-native";
|
151
187
|
let isRare = false;
|
152
|
-
if (
|
188
|
+
if (includeRPI && this.isRare()) {
|
153
189
|
isRare = true;
|
154
190
|
className += " rare";
|
155
191
|
}
|
156
192
|
const attributes = { class: className };
|
157
|
-
const link = HTML.wrap(
|
158
|
-
|
159
|
-
|
193
|
+
const link = HTML.wrap(
|
194
|
+
"span",
|
195
|
+
HTML.getLink(href, this.getName()),
|
196
|
+
attributes
|
197
|
+
);
|
198
|
+
if (isRare) {
|
199
|
+
return HTML.getToolTip(link, this.getRPIRankAndThreatTooltip(), {
|
200
|
+
icon: false,
|
201
|
+
});
|
160
202
|
}
|
161
203
|
return link;
|
162
204
|
}
|
@@ -167,7 +209,7 @@ class Taxon {
|
|
167
209
|
|
168
210
|
getINatName() {
|
169
211
|
const name = this.#iNatSyn ? this.#iNatSyn : this.getName();
|
170
|
-
return name.replace(
|
212
|
+
return name.replace(/ (subsp|var)\./, "").replace("×", "× ");
|
171
213
|
}
|
172
214
|
|
173
215
|
getINatSyn() {
|
@@ -176,11 +218,16 @@ class Taxon {
|
|
176
218
|
|
177
219
|
getINatTaxonLink() {
|
178
220
|
const iNatID = this.getINatID();
|
179
|
-
if (
|
221
|
+
if (!iNatID) {
|
180
222
|
return "";
|
181
223
|
}
|
182
|
-
const link = HTML.getLink(
|
183
|
-
|
224
|
+
const link = HTML.getLink(
|
225
|
+
"https://www.inaturalist.org/taxa/" + iNatID,
|
226
|
+
"iNaturalist",
|
227
|
+
{},
|
228
|
+
true
|
229
|
+
);
|
230
|
+
return this.#iNatSyn ? link + " (" + this.#iNatSyn + ")" : link;
|
184
231
|
}
|
185
232
|
|
186
233
|
getJepsonID() {
|
@@ -196,10 +243,10 @@ class Taxon {
|
|
196
243
|
}
|
197
244
|
|
198
245
|
getRPIRank() {
|
199
|
-
if (
|
246
|
+
if (!this.#rankRPI) {
|
200
247
|
return this.#rankRPI;
|
201
248
|
}
|
202
|
-
return this.#rankRPI.split(
|
249
|
+
return this.#rankRPI.split(".")[0];
|
203
250
|
}
|
204
251
|
|
205
252
|
getRPIRankAndThreat() {
|
@@ -207,15 +254,22 @@ class Taxon {
|
|
207
254
|
}
|
208
255
|
|
209
256
|
getRPIRankAndThreatTooltip() {
|
210
|
-
return RarePlants.getRPIRankAndThreatDescriptions(
|
257
|
+
return RarePlants.getRPIRankAndThreatDescriptions(
|
258
|
+
this.getRPIRankAndThreat()
|
259
|
+
).join("<br>");
|
211
260
|
}
|
212
261
|
|
213
262
|
getRPITaxonLink() {
|
214
263
|
const rpiID = this.getRPIID();
|
215
|
-
if (
|
264
|
+
if (!rpiID) {
|
216
265
|
return "";
|
217
266
|
}
|
218
|
-
const link = HTML.getLink(
|
267
|
+
const link = HTML.getLink(
|
268
|
+
"https://rareplants.cnps.org/Plants/Details/" + rpiID,
|
269
|
+
"CNPS Rare Plant Inventory",
|
270
|
+
{},
|
271
|
+
true
|
272
|
+
);
|
219
273
|
return link;
|
220
274
|
}
|
221
275
|
|
@@ -223,16 +277,20 @@ class Taxon {
|
|
223
277
|
return this.#status;
|
224
278
|
}
|
225
279
|
|
226
|
-
|
227
|
-
|
280
|
+
/**
|
281
|
+
* @param {*} config
|
282
|
+
* @returns
|
283
|
+
*/
|
284
|
+
getStatusDescription(config) {
|
285
|
+
switch (this.#status) {
|
228
286
|
case "N":
|
229
287
|
return "Native";
|
230
288
|
case "NC":
|
231
|
-
return config.getLabel(
|
289
|
+
return config.getLabel("status-NC", "Introduced");
|
232
290
|
case "X":
|
233
291
|
return "Introduced";
|
234
292
|
}
|
235
|
-
throw new Error(
|
293
|
+
throw new Error(this.#status);
|
236
294
|
}
|
237
295
|
|
238
296
|
getSynonyms() {
|
@@ -257,7 +315,7 @@ class Taxon {
|
|
257
315
|
|
258
316
|
shouldHaveFlowers() {
|
259
317
|
const sectionName = this.getFamily().getSectionName();
|
260
|
-
switch (
|
318
|
+
switch (sectionName) {
|
261
319
|
case "Ceratophyllales":
|
262
320
|
case "Eudicots":
|
263
321
|
case "Magnoliids":
|
@@ -269,9 +327,9 @@ class Taxon {
|
|
269
327
|
case "Lycophytes":
|
270
328
|
return false;
|
271
329
|
default:
|
272
|
-
throw new Error(
|
330
|
+
throw new Error(sectionName);
|
273
331
|
}
|
274
332
|
}
|
275
333
|
}
|
276
334
|
|
277
|
-
export { TAXA_COLNAMES, Taxon };
|
335
|
+
export { TAXA_COLNAMES, Taxon };
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "@ca-plant-list/ca-plant-list",
|
3
|
-
"version": "0.3.
|
3
|
+
"version": "0.3.4",
|
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": {
|
@@ -10,11 +10,7 @@
|
|
10
10
|
"homepage": "https://github.com/ca-plants/ca-plant-list",
|
11
11
|
"type": "module",
|
12
12
|
"exports": {
|
13
|
-
".": "./lib/index.js"
|
14
|
-
"./Families": "./lib/families.js",
|
15
|
-
"./Files": "./lib/files.js",
|
16
|
-
"./HTML": "./lib/html.js",
|
17
|
-
"./Jekyll": "./lib/jekyll.js"
|
13
|
+
".": "./lib/index.js"
|
18
14
|
},
|
19
15
|
"types": "./lib/index.d.ts",
|
20
16
|
"bin": {
|
@@ -35,6 +31,8 @@
|
|
35
31
|
},
|
36
32
|
"devDependencies": {
|
37
33
|
"@types/node": "^18.11.9",
|
34
|
+
"@types/unzipper": "^0.10.9",
|
35
|
+
"dts-bundle-generator": "^9.3.1",
|
38
36
|
"eslint": "^8.26.0",
|
39
37
|
"typescript": "^5.3.3"
|
40
38
|
}
|
package/scripts/build-ebook.js
CHANGED
@@ -6,64 +6,72 @@ import { Files } from "../lib/files.js";
|
|
6
6
|
import { TaxaProcessor } from "../lib/taxaprocessor.js";
|
7
7
|
import { CommandProcessor } from "../lib/commandprocessor.js";
|
8
8
|
|
9
|
-
const OPTION_DEFS = [
|
10
|
-
{ name: "locationsdir", type: String },
|
11
|
-
];
|
9
|
+
const OPTION_DEFS = [{ name: "locationsdir", type: String }];
|
12
10
|
|
13
11
|
const OPTION_HELP = [
|
14
12
|
{
|
15
13
|
name: "locationsdir",
|
16
14
|
type: String,
|
17
15
|
typeLabel: "{underline path}",
|
18
|
-
description:
|
19
|
-
|
20
|
-
|
16
|
+
description:
|
17
|
+
"If this option is specified, multiple ebooks will be generated. {bold locationsdir} must be a subdirectory" +
|
18
|
+
" of the current directory, and each subdirectory of {bold locationsdir} is processed in turn to generate an ebook." +
|
19
|
+
" Each ebook is placed in a subdirectory of {bold outputdir}.",
|
21
20
|
},
|
22
21
|
];
|
23
22
|
|
24
23
|
class BookCommand extends CommandProcessor {
|
25
|
-
|
26
24
|
constructor() {
|
27
|
-
super(
|
25
|
+
super(
|
26
|
+
"ca-plant-book",
|
27
|
+
"A tool to generate an ebook with local plant data.",
|
28
|
+
OPTION_DEFS,
|
29
|
+
OPTION_HELP
|
30
|
+
);
|
28
31
|
}
|
29
|
-
|
30
32
|
}
|
31
33
|
|
32
|
-
|
34
|
+
/**
|
35
|
+
* @param {TaxaProcessor} tp
|
36
|
+
*/
|
37
|
+
async function commandRunner(tp) {
|
33
38
|
const options = tp.getOptions();
|
34
|
-
const ebook = new PlantBook(
|
39
|
+
const ebook = new PlantBook(
|
40
|
+
options.outputdir,
|
41
|
+
new Config(options.datadir),
|
42
|
+
tp.getTaxa()
|
43
|
+
);
|
35
44
|
await ebook.create();
|
36
45
|
}
|
37
46
|
|
38
|
-
async function generateEBooks(
|
47
|
+
async function generateEBooks(options) {
|
39
48
|
const locationsDir = options.locationsdir;
|
40
49
|
|
41
50
|
// If there is a "locations" directory, generate a book for all subdirectories.
|
42
|
-
if (
|
51
|
+
if (locationsDir) {
|
43
52
|
// Generate ebook for each location.
|
44
53
|
const outputBase = options.outputdir;
|
45
|
-
const subdirs = Files.getDirEntries(
|
46
|
-
for (
|
47
|
-
console.log(
|
54
|
+
const subdirs = Files.getDirEntries(locationsDir);
|
55
|
+
for (const subdir of subdirs) {
|
56
|
+
console.log("Generating " + subdir);
|
48
57
|
const suffix = "/" + subdir;
|
49
58
|
const path = locationsDir + suffix;
|
50
|
-
if (
|
59
|
+
if (Files.isDir(path)) {
|
51
60
|
options.datadir = path;
|
52
61
|
options.outputdir = outputBase + suffix;
|
53
|
-
const gen = new TaxaProcessor(
|
54
|
-
await gen.process(
|
62
|
+
const gen = new TaxaProcessor(options);
|
63
|
+
await gen.process(commandRunner);
|
55
64
|
}
|
56
65
|
}
|
57
66
|
} else {
|
58
67
|
// Otherwise use the default directory.
|
59
|
-
const gen = new TaxaProcessor(
|
60
|
-
await gen.process(
|
68
|
+
const gen = new TaxaProcessor(options);
|
69
|
+
await gen.process(commandRunner);
|
61
70
|
}
|
62
|
-
|
63
71
|
}
|
64
72
|
|
65
73
|
const cmd = new BookCommand();
|
66
74
|
const options = cmd.getOptions();
|
67
|
-
if (
|
68
|
-
generateEBooks(
|
69
|
-
}
|
75
|
+
if (!options.help) {
|
76
|
+
await generateEBooks(options);
|
77
|
+
}
|