@ca-plant-list/ca-plant-list 0.3.2 → 0.3.3
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/ovary.md +3 -0
- package/data/glossary/pistil.md +3 -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 -2
- package/data/taxa.csv +54 -52
- package/data/text/Ceanothus-cuneatus-var-cuneatus.md +1 -0
- package/data/text/Ceanothus-leucodermis.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/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 -28
- package/lib/dateutils.js +36 -16
- package/lib/ebook/pages/taxonpage.js +63 -36
- package/lib/errorlog.js +16 -11
- package/lib/families.js +109 -71
- package/lib/files.js +103 -45
- package/lib/generictaxaloader.js +15 -7
- package/lib/index.js +38 -3
- package/lib/taxa.js +164 -87
- package/lib/taxaloader.js +28 -15
- package/lib/taxaprocessor.js +6 -8
- package/lib/taxon.js +109 -56
- package/package.json +4 -6
- package/scripts/build-ebook.js +30 -25
- package/lib/index.d.ts +0 -345
package/lib/taxa.js
CHANGED
@@ -1,8 +1,8 @@
|
|
1
1
|
import { Config } from "./config.js";
|
2
|
-
import { Taxon } from "./taxon.js";
|
3
2
|
import { HTML } from "./html.js";
|
4
3
|
import { CSV } from "./csv.js";
|
5
4
|
import { RarePlants } from "./rareplants.js";
|
5
|
+
import { ErrorLog, Taxon } from "./index.js";
|
6
6
|
|
7
7
|
const FLOWER_COLORS = [
|
8
8
|
{ name: "white", color: "white" },
|
@@ -14,50 +14,65 @@ const FLOWER_COLORS = [
|
|
14
14
|
{ name: "purple", color: "purple" },
|
15
15
|
];
|
16
16
|
|
17
|
+
/**
|
18
|
+
* @type {Object.<string,import("./index.js").TaxaCol>}
|
19
|
+
*/
|
17
20
|
const TAXA_LIST_COLS = {
|
18
21
|
CESA: {
|
19
22
|
title: "California",
|
20
|
-
data: (
|
23
|
+
data: (t) => RarePlants.getCESADescription(t.getCESA()),
|
21
24
|
},
|
22
25
|
COMMON_NAME: {
|
23
26
|
title: "Common Name",
|
24
|
-
data: (
|
27
|
+
data: (t) => t.getCommonNames().join(", "),
|
25
28
|
},
|
26
29
|
CNPS_RANK: {
|
27
30
|
title: "CNPS Rank",
|
28
|
-
data: (
|
31
|
+
data: (t) =>
|
32
|
+
HTML.getToolTip(
|
33
|
+
HTML.textElement("span", t.getRPIRankAndThreat()),
|
34
|
+
t.getRPIRankAndThreatTooltip()
|
35
|
+
),
|
29
36
|
},
|
30
37
|
FESA: {
|
31
38
|
title: "Federal",
|
32
|
-
data: (
|
39
|
+
data: (t) => RarePlants.getFESADescription(t.getFESA()),
|
33
40
|
},
|
34
41
|
SPECIES: {
|
35
42
|
title: "Species",
|
36
|
-
data: (
|
43
|
+
data: (t) => t.getHTMLLink(true, true),
|
37
44
|
},
|
38
45
|
SPECIES_BARE: {
|
39
46
|
title: "Species",
|
40
|
-
data: (
|
47
|
+
data: (t) => t.getHTMLLink(true, false),
|
41
48
|
},
|
42
49
|
};
|
43
50
|
|
44
|
-
const DEFAULT_COLUMNS = [
|
51
|
+
const DEFAULT_COLUMNS = [TAXA_LIST_COLS.SPECIES, TAXA_LIST_COLS.COMMON_NAME];
|
45
52
|
|
46
53
|
class FlowerColor {
|
47
|
-
|
48
54
|
#color;
|
55
|
+
/** @type {Taxon[]} */
|
49
56
|
#taxa = [];
|
50
57
|
|
51
|
-
|
58
|
+
/**
|
59
|
+
* @param {string} color
|
60
|
+
*/
|
61
|
+
constructor(color) {
|
52
62
|
this.#color = color;
|
53
63
|
}
|
54
64
|
|
55
|
-
|
56
|
-
|
65
|
+
/**
|
66
|
+
* @param {Taxon} taxon
|
67
|
+
*/
|
68
|
+
addTaxon(taxon) {
|
69
|
+
this.#taxa.push(taxon);
|
57
70
|
}
|
58
71
|
|
59
|
-
getColorName(
|
60
|
-
return uc
|
72
|
+
getColorName(uc = false) {
|
73
|
+
return uc
|
74
|
+
? this.#color[0].toUpperCase() + this.#color.substring(1)
|
75
|
+
: this.#color;
|
61
76
|
}
|
62
77
|
|
63
78
|
getFileName() {
|
@@ -67,67 +82,100 @@ class FlowerColor {
|
|
67
82
|
getTaxa() {
|
68
83
|
return this.#taxa;
|
69
84
|
}
|
70
|
-
|
71
85
|
}
|
72
86
|
|
73
87
|
class Taxa {
|
74
|
-
|
75
88
|
#errorLog;
|
89
|
+
/** @type {Object<string,Taxon>} */
|
76
90
|
#taxa = {};
|
91
|
+
/** @type {Object<string,FlowerColor>} */
|
77
92
|
#flower_colors = {};
|
78
93
|
#sortedTaxa;
|
79
94
|
#synonyms = new Set();
|
80
95
|
#isSubset;
|
81
96
|
|
82
|
-
|
83
|
-
|
84
|
-
|
97
|
+
/**
|
98
|
+
*
|
99
|
+
* @param {*} inclusionList
|
100
|
+
* @param {ErrorLog} errorLog
|
101
|
+
* @param {*} showFlowerErrors
|
102
|
+
* @param {*} taxaMeta
|
103
|
+
* @param {*} taxonClass
|
104
|
+
* @param {*} extraTaxa
|
105
|
+
* @param {*} extraSynonyms
|
106
|
+
*/
|
107
|
+
constructor(
|
108
|
+
inclusionList,
|
109
|
+
errorLog,
|
110
|
+
showFlowerErrors,
|
111
|
+
taxaMeta = {},
|
112
|
+
taxonClass = Taxon,
|
113
|
+
extraTaxa = [],
|
114
|
+
extraSynonyms = []
|
115
|
+
) {
|
116
|
+
this.#isSubset = inclusionList !== true;
|
85
117
|
|
86
118
|
this.#errorLog = errorLog;
|
87
119
|
|
88
|
-
for (
|
89
|
-
this.#flower_colors[
|
120
|
+
for (const color of FLOWER_COLORS) {
|
121
|
+
this.#flower_colors[color.name] = new FlowerColor(color.name);
|
90
122
|
}
|
91
123
|
|
92
124
|
const dataDir = Config.getPackageDir() + "/data";
|
93
125
|
|
94
|
-
const taxaCSV = CSV.parseFile(
|
95
|
-
this.#loadTaxa(
|
96
|
-
|
126
|
+
const taxaCSV = CSV.parseFile(dataDir, "taxa.csv");
|
127
|
+
this.#loadTaxa(
|
128
|
+
taxaCSV,
|
129
|
+
inclusionList,
|
130
|
+
taxaMeta,
|
131
|
+
taxonClass,
|
132
|
+
showFlowerErrors
|
133
|
+
);
|
134
|
+
this.#loadTaxa(
|
135
|
+
extraTaxa,
|
136
|
+
inclusionList,
|
137
|
+
taxaMeta,
|
138
|
+
taxonClass,
|
139
|
+
showFlowerErrors
|
140
|
+
);
|
97
141
|
|
98
142
|
// Make sure everything in the inclusionList has been loaded.
|
99
|
-
for (
|
100
|
-
if (
|
101
|
-
this.#errorLog.log(
|
143
|
+
for (const name of Object.keys(inclusionList)) {
|
144
|
+
if (!this.getTaxon(name)) {
|
145
|
+
this.#errorLog.log(name, "not found in taxon list");
|
102
146
|
}
|
103
147
|
}
|
104
148
|
|
105
|
-
this.#sortedTaxa = Object.values(
|
106
|
-
|
107
|
-
|
108
|
-
this.#loadSyns( synCSV, inclusionList );
|
109
|
-
this.#loadSyns( extraSynonyms, inclusionList );
|
149
|
+
this.#sortedTaxa = Object.values(this.#taxa).sort((a, b) =>
|
150
|
+
a.getName().localeCompare(b.getName())
|
151
|
+
);
|
110
152
|
|
153
|
+
const synCSV = CSV.parseFile(dataDir, "synonyms.csv");
|
154
|
+
this.#loadSyns(synCSV, inclusionList);
|
155
|
+
this.#loadSyns(extraSynonyms, inclusionList);
|
111
156
|
}
|
112
157
|
|
113
|
-
|
114
|
-
|
158
|
+
/**
|
159
|
+
* @param {Taxon[]} taxa
|
160
|
+
* @param {import("./index.js").TaxaCol[]} columns
|
161
|
+
*/
|
162
|
+
static getHTMLTable(taxa, columns = DEFAULT_COLUMNS) {
|
115
163
|
let html = "<table><thead>";
|
116
|
-
for (
|
164
|
+
for (const col of columns) {
|
117
165
|
const className = col.class;
|
118
166
|
const atts = className ? { class: className } : {};
|
119
|
-
html += HTML.textElement(
|
167
|
+
html += HTML.textElement("th", col.title, atts);
|
120
168
|
}
|
121
169
|
html += "</thead>";
|
122
170
|
html += "<tbody>";
|
123
171
|
|
124
|
-
for (
|
172
|
+
for (const taxon of taxa) {
|
125
173
|
html += "<tr>";
|
126
|
-
for (
|
127
|
-
const data = col.data(
|
174
|
+
for (const col of columns) {
|
175
|
+
const data = col.data(taxon);
|
128
176
|
const className = col.class;
|
129
177
|
const atts = className ? { class: className } : {};
|
130
|
-
html += HTML.wrap(
|
178
|
+
html += HTML.wrap("td", data, atts);
|
131
179
|
}
|
132
180
|
html += "</tr>";
|
133
181
|
}
|
@@ -138,28 +186,37 @@ class Taxa {
|
|
138
186
|
return html;
|
139
187
|
}
|
140
188
|
|
141
|
-
|
142
|
-
|
189
|
+
/**
|
190
|
+
* @param {string} name
|
191
|
+
*/
|
192
|
+
getFlowerColor(name) {
|
193
|
+
return this.#flower_colors[name];
|
143
194
|
}
|
144
195
|
|
145
196
|
static getFlowerColorNames() {
|
146
|
-
return FLOWER_COLORS.map(
|
197
|
+
return FLOWER_COLORS.map((o) => o.name);
|
147
198
|
}
|
148
199
|
|
149
200
|
static getFlowerColors() {
|
150
201
|
return FLOWER_COLORS;
|
151
202
|
}
|
152
203
|
|
153
|
-
|
154
|
-
|
204
|
+
/**
|
205
|
+
* @param {string} name
|
206
|
+
*/
|
207
|
+
getTaxon(name) {
|
208
|
+
return this.#taxa[name];
|
155
209
|
}
|
156
210
|
|
157
211
|
getTaxonList() {
|
158
212
|
return this.#sortedTaxa;
|
159
213
|
}
|
160
214
|
|
161
|
-
|
162
|
-
|
215
|
+
/**
|
216
|
+
* @param {string} formerName
|
217
|
+
*/
|
218
|
+
hasSynonym(formerName) {
|
219
|
+
return this.#synonyms.has(formerName);
|
163
220
|
}
|
164
221
|
|
165
222
|
/**
|
@@ -170,75 +227,95 @@ class Taxa {
|
|
170
227
|
return this.#isSubset;
|
171
228
|
}
|
172
229
|
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
230
|
+
/**
|
231
|
+
* @param {*} synCSV
|
232
|
+
* @param {*} inclusionList
|
233
|
+
*/
|
234
|
+
#loadSyns(synCSV, inclusionList) {
|
235
|
+
for (const syn of synCSV) {
|
236
|
+
const currName = syn["Current"];
|
237
|
+
const taxon = this.getTaxon(currName);
|
238
|
+
if (!taxon) {
|
239
|
+
if (inclusionList === true && !syn.Type) {
|
179
240
|
// If including all taxa, note the error - the target is not defined, and this is not
|
180
241
|
// a synonym for a non-Jepson system.
|
181
|
-
console.log(
|
242
|
+
console.log("synonym target not found: " + currName);
|
182
243
|
}
|
183
244
|
continue;
|
184
245
|
}
|
185
|
-
const formerName = syn[
|
186
|
-
this.#synonyms.add(
|
187
|
-
taxon.addSynonym(
|
246
|
+
const formerName = syn["Former"];
|
247
|
+
this.#synonyms.add(formerName);
|
248
|
+
taxon.addSynonym(formerName, syn["Type"]);
|
188
249
|
}
|
189
250
|
}
|
190
251
|
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
252
|
+
/**
|
253
|
+
* @param {*} taxaCSV
|
254
|
+
* @param {*} inclusionList
|
255
|
+
* @param {*} taxaMeta
|
256
|
+
* @param {Taxon} taxonClass
|
257
|
+
* @param {boolean} showFlowerErrors
|
258
|
+
*/
|
259
|
+
#loadTaxa(taxaCSV, inclusionList, taxaMeta, taxonClass, showFlowerErrors) {
|
260
|
+
for (const row of taxaCSV) {
|
261
|
+
const name = row["taxon_name"];
|
195
262
|
|
196
263
|
let taxon_overrides = {};
|
197
|
-
if (
|
198
|
-
taxon_overrides = inclusionList[
|
199
|
-
if (
|
264
|
+
if (inclusionList !== true) {
|
265
|
+
taxon_overrides = inclusionList[name];
|
266
|
+
if (!taxon_overrides) {
|
200
267
|
continue;
|
201
268
|
}
|
202
269
|
}
|
203
270
|
|
204
|
-
if (
|
205
|
-
this.#errorLog.log(
|
271
|
+
if (this.#taxa[name]) {
|
272
|
+
this.#errorLog.log(name, "has multiple entries");
|
206
273
|
}
|
207
274
|
|
208
|
-
const status = taxon_overrides[
|
209
|
-
if (
|
210
|
-
row[
|
275
|
+
const status = taxon_overrides["status"];
|
276
|
+
if (status !== undefined) {
|
277
|
+
row["status"] = status;
|
211
278
|
}
|
212
|
-
const taxon = new taxonClass(
|
213
|
-
this.#taxa[
|
279
|
+
const taxon = new taxonClass(row, taxaMeta[name]);
|
280
|
+
this.#taxa[name] = taxon;
|
214
281
|
const colors = taxon.getFlowerColors();
|
215
|
-
if (
|
216
|
-
for (
|
217
|
-
const color = this.#flower_colors[
|
218
|
-
if (
|
219
|
-
throw new Error(
|
282
|
+
if (colors) {
|
283
|
+
for (const colorName of colors) {
|
284
|
+
const color = this.#flower_colors[colorName];
|
285
|
+
if (!color) {
|
286
|
+
throw new Error(
|
287
|
+
'flower color "' + colorName + '" not found'
|
288
|
+
);
|
220
289
|
}
|
221
|
-
color.addTaxon(
|
290
|
+
color.addTaxon(taxon);
|
222
291
|
}
|
223
292
|
}
|
224
293
|
|
225
|
-
if (
|
294
|
+
if (showFlowerErrors) {
|
226
295
|
// Make sure flower colors/bloom times are present or not depending on taxon.
|
227
|
-
if (
|
228
|
-
if (
|
229
|
-
|
296
|
+
if (taxon.shouldHaveFlowers()) {
|
297
|
+
if (
|
298
|
+
!colors ||
|
299
|
+
!taxon.getBloomStart() ||
|
300
|
+
!taxon.getBloomEnd()
|
301
|
+
) {
|
302
|
+
this.#errorLog.log(
|
303
|
+
name,
|
304
|
+
"does not have all flower info"
|
305
|
+
);
|
230
306
|
}
|
231
307
|
} else {
|
232
|
-
if (
|
233
|
-
|
308
|
+
if (
|
309
|
+
colors ||
|
310
|
+
taxon.getBloomStart() ||
|
311
|
+
taxon.getBloomEnd()
|
312
|
+
) {
|
313
|
+
this.#errorLog.log(name, "should not have flower info");
|
234
314
|
}
|
235
315
|
}
|
236
316
|
}
|
237
317
|
}
|
238
|
-
|
239
318
|
}
|
240
|
-
|
241
|
-
|
242
319
|
}
|
243
320
|
|
244
|
-
export { Taxa, TAXA_LIST_COLS };
|
321
|
+
export { Taxa, TAXA_LIST_COLS };
|
package/lib/taxaloader.js
CHANGED
@@ -1,35 +1,48 @@
|
|
1
|
-
import { CSV } from "./csv.js";
|
2
|
-
import { Files } from "./files.js";
|
3
1
|
import { GenericTaxaLoader } from "./generictaxaloader.js";
|
4
|
-
import { Taxa } from "./
|
2
|
+
import { CSV, Files, Taxa } from "./index.js";
|
5
3
|
|
6
4
|
class TaxaLoader extends GenericTaxaLoader {
|
7
|
-
|
8
|
-
|
9
|
-
|
5
|
+
/**
|
6
|
+
* @param {*} options
|
7
|
+
*/
|
8
|
+
constructor(options) {
|
9
|
+
super(options);
|
10
10
|
}
|
11
11
|
|
12
|
+
/**
|
13
|
+
* @return {Promise<Taxa>}
|
14
|
+
*/
|
12
15
|
async loadTaxa() {
|
13
|
-
|
16
|
+
/**
|
17
|
+
*
|
18
|
+
* @param {string} dataDir
|
19
|
+
* @returns
|
20
|
+
*/
|
21
|
+
function getIncludeList(dataDir) {
|
14
22
|
// Read inclusion list.
|
15
23
|
const includeFileName = "taxa_include.csv";
|
16
24
|
const includeFilePath = dataDir + "/" + includeFileName;
|
17
|
-
if (
|
18
|
-
console.log(
|
25
|
+
if (!Files.exists(includeFilePath)) {
|
26
|
+
console.log(includeFilePath + " not found; loading all taxa");
|
19
27
|
return true;
|
20
28
|
}
|
21
|
-
|
29
|
+
/**@type { import("./index.js").TaxonData[]} */
|
30
|
+
const includeCSV = CSV.parseFile(dataDir, includeFileName);
|
31
|
+
/** @type {Object<string,import("./index.js").TaxonData>} */
|
22
32
|
const include = {};
|
23
|
-
for (
|
24
|
-
include[
|
33
|
+
for (const row of includeCSV) {
|
34
|
+
include[row["taxon_name"]] = row;
|
25
35
|
}
|
26
36
|
return include;
|
27
37
|
}
|
28
38
|
|
29
39
|
const options = this.getOptions();
|
30
|
-
return new Taxa(
|
40
|
+
return new Taxa(
|
41
|
+
getIncludeList(options.datadir),
|
42
|
+
this.getErrorLog(),
|
43
|
+
options["show-flower-errors"]
|
44
|
+
);
|
31
45
|
}
|
32
|
-
|
33
46
|
}
|
34
47
|
|
35
|
-
export { TaxaLoader };
|
48
|
+
export { TaxaLoader };
|
package/lib/taxaprocessor.js
CHANGED
@@ -1,12 +1,11 @@
|
|
1
1
|
import { TaxaLoader } from "./taxaloader.js";
|
2
2
|
|
3
3
|
class TaxaProcessor {
|
4
|
-
|
5
4
|
#options;
|
6
5
|
#taxaLoaderClass;
|
7
6
|
#taxaLoader;
|
8
7
|
|
9
|
-
constructor(
|
8
|
+
constructor(options, taxaLoaderClass = TaxaLoader) {
|
10
9
|
this.#options = options;
|
11
10
|
this.#taxaLoaderClass = taxaLoaderClass;
|
12
11
|
}
|
@@ -23,14 +22,13 @@ class TaxaProcessor {
|
|
23
22
|
return this.#taxaLoader.getTaxa();
|
24
23
|
}
|
25
24
|
|
26
|
-
async process(
|
27
|
-
console.log(
|
28
|
-
this.#taxaLoader = new this.#taxaLoaderClass(
|
25
|
+
async process(commandRunner) {
|
26
|
+
console.log("loading data");
|
27
|
+
this.#taxaLoader = new this.#taxaLoaderClass(this.#options);
|
29
28
|
await this.#taxaLoader.load();
|
29
|
+
await commandRunner(this);
|
30
30
|
this.#taxaLoader.writeErrorLog();
|
31
|
-
await commandRunner( this );
|
32
31
|
}
|
33
|
-
|
34
32
|
}
|
35
33
|
|
36
|
-
export { TaxaProcessor };
|
34
|
+
export { TaxaProcessor };
|