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