@ca-plant-list/ca-plant-list 0.4.29 → 0.4.31
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 +113 -346
- package/data/synonyms.csv +122 -119
- package/data/taxa.csv +8 -6
- package/lib/csv.js +3 -4
- package/lib/externalsites.js +15 -9
- package/lib/htmltaxon.js +44 -1
- package/lib/index.d.ts +16 -4
- package/lib/index.js +2 -0
- package/lib/photo.js +10 -1
- package/lib/tools/inat.js +33 -4
- package/lib/util.js +6 -3
- package/lib/utils/httpUtils.js +10 -0
- package/lib/utils/inat-tools.js +6 -9
- package/lib/web/pageGeneric.js +1 -2
- package/package.json +1 -1
- package/scripts/cpl-photos.js +200 -18
package/data/taxa.csv
CHANGED
@@ -94,7 +94,8 @@ Amsinckia grandiflora,large-flowered fiddleneck,N,13130,323,75424,202030,,Large-
|
|
94
94
|
Amsinckia intermedia,fiddleneck,N,13131,11324,49141,202031,,Common Fiddleneck
|
95
95
|
Amsinckia lunaris,bent-flower fiddleneck,N,13139,324,75425,202032,,Bent Flowered Fiddleneck,,,,,5,1B.2,,,S3,G3
|
96
96
|
Amsinckia lycopsoides,,N,13140,325,58048,202033,,Bugloss-flowered Fiddleneck
|
97
|
-
Amsinckia menziesii,rancher's fireweed,N,13145,326,55436,202034,,Menzies' Fiddleneck
|
97
|
+
Amsinckia menziesii,rancher's fireweed,N,13145,326,55436,202034,,Menzies' Fiddleneck,annual,orange
|
98
|
+
Amsinckia retrorsa,rigid fiddleneck,N,13147,11325,58049,202037,,Rigid Fiddleneck,annual,yellow,2,5
|
98
99
|
Amsinckia tessellata var. gloriosa,,N,77019,333,80365,202042,,Carrizo Fiddleneck
|
99
100
|
Amsinckia tessellata var. tessellata,devil's lettuce,N,77020,334,58852,202043,,Desert Fiddleneck
|
100
101
|
Amsinckia vernicosa,,N,13155,335,58051,202027,,Green Fiddleneck
|
@@ -207,7 +208,8 @@ Balsamorhiza deltoidea,,N,1634,1047,70383,202414,true,Balsam Deltoid
|
|
207
208
|
Balsamorhiza macrolepis,,N,1639,1051,69665,211633,true,California Balsamroot,,,,,350,1B.2,,,S2,G2
|
208
209
|
Barbarea orthoceras,winter cress,N,15481,1057,52976,11356,true,American Wintercress,,yellow,3,7
|
209
210
|
Bassia hyssopifolia,,X,15511,1060,58125,109432,true
|
210
|
-
Bellardia trixago var. trixago
|
211
|
+
Bellardia trixago var. trixago,Mediterranean lineseed,X,8635,14706,1115038,416937,Bellardia trixago,,annual,"white,pink",4,8
|
212
|
+
Bellardia viscosa,yellow glandweed,X,103729,14707,537967,206574,true,,annual,yellow,4,10
|
211
213
|
Bellis perennis,English daisy,X,1652,1065,55563,202429,true
|
212
214
|
Berberis aquifolium var. aquifolium,,N,71483,1068,126887,45198,Berberis aquifolium,Piper's Barberry
|
213
215
|
Berberis aquifolium var. dictyota,,N,76967,1069,775436,45199,Berberis dictyota,Shining Netvein Barberry
|
@@ -636,7 +638,7 @@ Elymus condensatus,giant wild-rye,N,24165,11636,164673,140055,Leymus condensatus
|
|
636
638
|
Elymus elymoides var. elymoides,squirreltail,N,72180,11639,58377,211223,Elymus elymoides subsp. elymoides,Squirreltail
|
637
639
|
Elymus glaucus subsp. glaucus,blue wildrye,N,50264,2937,57171,167143,true,Western Rye Grass
|
638
640
|
Elymus glaucus subsp. virescens,,N,50266,2939,61203,206754,true,Blue Wildrye
|
639
|
-
Elymus hispidus,wheatgrass,X,24153,11640,155848,140096,Thinopyrum intermedium
|
641
|
+
Elymus hispidus,wheatgrass,X,24153,11640,155848,140096,Thinopyrum intermedium
|
640
642
|
Elymus mollis subsp. mollis,,N,70142,11641,118426,206755,Leymus mollis subsp. mollis,Dune Wild Rye
|
641
643
|
Elymus multisetus,big squirreltail,N,24122,2941,57167,140134,true,Big Squirreltail
|
642
644
|
Elymus ponticus,tall wheatgrass,X,91896,11643,1301269,206756,Thinopyrum ponticum
|
@@ -783,7 +785,7 @@ Festuca elmeri,Elmer fescue,N,25798,3580,64333,141011,true,Elmer Fescue
|
|
783
785
|
Festuca idahoensis,Idaho fescue,N,25808,3581,61200,141076,true,Idaho Fescue
|
784
786
|
Festuca microstachys,,N,25865,11688,77138,141123,Vulpia microstachys var. ciliata,Pacific Fescue
|
785
787
|
Festuca myuros,zorro grass,X,25869,11689,77140,141133,Vulpia myuros
|
786
|
-
Festuca octoflora,six-week's fescue,N,25872,11690,77141,141148,Vulpia octoflora
|
788
|
+
Festuca octoflora,six-week's fescue,N,25872,11690,77141,141148,Vulpia octoflora,Six Weeks Fescue
|
787
789
|
Festuca perennis,Italian ryegrass,X,91964,11691,52801,203852,Lolium multiflorum
|
788
790
|
Festuca rubra,red fescue,N,25825,3586,61061,141223,true,Red Fescue
|
789
791
|
Festuca temulenta,darnel,X,93753,11692,164712,203853,Lolium temulentum
|
@@ -1557,7 +1559,7 @@ Romulea rosea var. australis,,X,65230,7162,68895,207249
|
|
1557
1559
|
Rorippa curvisiliqua,yellow cress,N,41580,7168,58866,30089,true,Curvepod Yellowcress
|
1558
1560
|
Rorippa palustris subsp. palustris,,N,52673,11988,64157,43562,true,Bog Yellowcress
|
1559
1561
|
Rosa californica,California rose,N,41631,7179,53437,30158,true,California Wildrose,,pink
|
1560
|
-
Rosa gymnocarpa,wood rose,N,41652,7182,53441,30212,true,
|
1562
|
+
Rosa gymnocarpa,wood rose,N,41652,7182,53441,30212,true,Wood Rose,,pink
|
1561
1563
|
Rosa rubiginosa,sweet-brier,X,41648,11057,78886,30313,true
|
1562
1564
|
Rosa spithamea,ground rose,N,41696,7187,53442,30338,true,Ground Rose
|
1563
1565
|
Rubus armeniacus,Himalayan blackberry,X,87465,10319,61317,30459
|
@@ -1737,7 +1739,7 @@ Stephanomeria virgata subsp. pleurocarpa,,N,6227,7806,58849,206092,true,Wand Wir
|
|
1737
1739
|
Stipa cernua,nodding needlegrass,N,45640,12042,165654,149288,Nassella cernua,Nodding Needle Grass
|
1738
1740
|
Stipa lemmonii var. lemmonii,Lemmon's needlegrass,N,73899,12053,1220756,173006,Achnatherum lemmonii subsp. lemmonii
|
1739
1741
|
Stipa lepida,foothill needlegrass,N,45641,12054,165656,149399,Nassella lepida,Foothill Needlegrass
|
1740
|
-
Stipa miliacea var. miliacea,smilo grass,X,91920,12058,524183,206097,Piptatherum miliaceum
|
1742
|
+
Stipa miliacea var. miliacea,smilo grass,X,91920,12058,524183,206097,Piptatherum miliaceum
|
1741
1743
|
Stipa pulchra,purple needlegrass,N,45644,12067,165659,149498,Nassella pulchra,Purple Needlegrass
|
1742
1744
|
Streptanthus albidus subsp. peramoenus,most beautiful jewelflower,N,53142,7814,58783,258754,Streptanthus glandulosus subsp. glandulosus,,,,,,1490,1B.2,,,S2,G2T2
|
1743
1745
|
Streptanthus breweri,,N,45725,7822,79258,32606,true,Brewer's Jewelflower
|
package/lib/csv.js
CHANGED
@@ -5,7 +5,7 @@ import { parse as parseSync } from "csv-parse/sync";
|
|
5
5
|
import { parse } from "csv-parse";
|
6
6
|
import { stringify } from "csv-stringify/sync";
|
7
7
|
|
8
|
-
class CSV {
|
8
|
+
export class CSV {
|
9
9
|
/**
|
10
10
|
* @param {string} fileName
|
11
11
|
* @param {import("csv-parse").ColumnOption[]|boolean|function (string[]):string[]} columns
|
@@ -95,10 +95,11 @@ class CSV {
|
|
95
95
|
}
|
96
96
|
|
97
97
|
/**
|
98
|
+
* @template T
|
98
99
|
* @param {string} fileName
|
99
100
|
* @param {boolean|import("csv-parse").ColumnOption[]|function (string[]):string[]} [columns]
|
100
101
|
* @param {string} [delimiter]
|
101
|
-
* @returns {
|
102
|
+
* @returns {T[]}
|
102
103
|
*/
|
103
104
|
static readFile(fileName, columns = true, delimiter) {
|
104
105
|
const content = fs.readFileSync(fileName);
|
@@ -155,5 +156,3 @@ class CSV {
|
|
155
156
|
fs.writeFileSync(fileName, content.replaceAll(/,+\n/g, "\n"));
|
156
157
|
}
|
157
158
|
}
|
158
|
-
|
159
|
-
export { CSV };
|
package/lib/externalsites.js
CHANGED
@@ -88,20 +88,26 @@ export class ExternalSites {
|
|
88
88
|
}
|
89
89
|
|
90
90
|
/**
|
91
|
-
* @param {
|
91
|
+
* @param {{taxon_id?:string,taxon_name?:string,subview?:"map"|"table",view?:"species"}} params
|
92
92
|
* @param {import("./config.js").Config} config
|
93
93
|
* @returns {URL|undefined}
|
94
94
|
*/
|
95
|
-
static getInatObsLink(
|
96
|
-
const iNatID = taxon.getINatID();
|
97
|
-
if (!iNatID) {
|
98
|
-
return;
|
99
|
-
}
|
100
|
-
|
95
|
+
static getInatObsLink(params, config) {
|
101
96
|
const url = new URL(
|
102
|
-
"https://www.inaturalist.org/observations?subview=map",
|
97
|
+
"https://www.inaturalist.org/observations?subview=map&iconic_taxa=Plantae",
|
103
98
|
);
|
104
|
-
|
99
|
+
if (params.subview) {
|
100
|
+
url.searchParams.set("subview", params.subview);
|
101
|
+
}
|
102
|
+
if (params.taxon_id) {
|
103
|
+
url.searchParams.set("taxon_id", params.taxon_id);
|
104
|
+
}
|
105
|
+
if (params.taxon_name) {
|
106
|
+
url.searchParams.set("taxon_name", params.taxon_name);
|
107
|
+
}
|
108
|
+
if (params.view) {
|
109
|
+
url.searchParams.set("view", params.view);
|
110
|
+
}
|
105
111
|
for (const p of ["place_id", "project_id"]) {
|
106
112
|
const v = config.getConfigValue("inat", p);
|
107
113
|
if (v) {
|
package/lib/htmltaxon.js
CHANGED
@@ -60,7 +60,11 @@ const OBSLINKS = {
|
|
60
60
|
},
|
61
61
|
inat: {
|
62
62
|
label: "iNaturalist",
|
63
|
-
href: (taxon, config) =>
|
63
|
+
href: (taxon, config) =>
|
64
|
+
ExternalSites.getInatObsLink(
|
65
|
+
{ taxon_id: taxon.getINatID() },
|
66
|
+
config,
|
67
|
+
),
|
64
68
|
},
|
65
69
|
};
|
66
70
|
|
@@ -120,6 +124,45 @@ class HTMLTaxon {
|
|
120
124
|
*/
|
121
125
|
static addObsLink(links, taxon, config, sourceCode, label) {
|
122
126
|
const source = OBSLINKS[sourceCode];
|
127
|
+
if (sourceCode === "inat") {
|
128
|
+
const iNatName = taxon.getINatName().split(" ");
|
129
|
+
/** @type {string[]} */
|
130
|
+
const iNatLinks = [];
|
131
|
+
iNatLinks.push(
|
132
|
+
HTML.getLink(
|
133
|
+
ExternalSites.getInatObsLink(
|
134
|
+
{ taxon_name: iNatName[0], view: "species" },
|
135
|
+
config,
|
136
|
+
),
|
137
|
+
`Genus ${iNatName[0]}`,
|
138
|
+
undefined,
|
139
|
+
true,
|
140
|
+
),
|
141
|
+
);
|
142
|
+
if (iNatName.length > 2) {
|
143
|
+
const species = `${iNatName[0]} ${iNatName[1]}`;
|
144
|
+
iNatLinks.push(
|
145
|
+
HTML.getLink(
|
146
|
+
ExternalSites.getInatObsLink(
|
147
|
+
{ taxon_name: species },
|
148
|
+
config,
|
149
|
+
),
|
150
|
+
species,
|
151
|
+
undefined,
|
152
|
+
true,
|
153
|
+
),
|
154
|
+
);
|
155
|
+
}
|
156
|
+
this.addLink(
|
157
|
+
iNatLinks,
|
158
|
+
source.href(taxon, config),
|
159
|
+
taxon.getINatSyn() ?? taxon.getName(),
|
160
|
+
);
|
161
|
+
|
162
|
+
const html = HTML.textElement("span", label ?? source.label);
|
163
|
+
links.push(html + HTML.wrap("ul", HTML.arrayToLI(iNatLinks)));
|
164
|
+
return;
|
165
|
+
}
|
123
166
|
this.addLink(links, source.href(taxon, config), label ?? source.label);
|
124
167
|
}
|
125
168
|
|
package/lib/index.d.ts
CHANGED
@@ -82,10 +82,7 @@ export class Config {
|
|
82
82
|
}
|
83
83
|
|
84
84
|
export class CSV {
|
85
|
-
static readFile(
|
86
|
-
fileName: string,
|
87
|
-
delimeter?: string,
|
88
|
-
): Record<string, string>[];
|
85
|
+
static readFile<T>(fileName: string, delimeter?: string): T[];
|
89
86
|
static writeFileObject(
|
90
87
|
fileName: string,
|
91
88
|
data: Record<string, any>[],
|
@@ -129,6 +126,21 @@ export class Files {
|
|
129
126
|
|
130
127
|
export class Genera {}
|
131
128
|
|
129
|
+
export class GenericPage {
|
130
|
+
constructor(
|
131
|
+
siteGenerator: SiteGenerator,
|
132
|
+
title: string,
|
133
|
+
baseFileName: string,
|
134
|
+
js?: string,
|
135
|
+
);
|
136
|
+
getBaseFileName(): string;
|
137
|
+
getFrontMatter(): string;
|
138
|
+
getOutputDir(): string;
|
139
|
+
getSiteGenerator(): SiteGenerator;
|
140
|
+
getTitle(): string;
|
141
|
+
writeFile(content: string): void;
|
142
|
+
}
|
143
|
+
|
132
144
|
export class Genus<T extends Taxon> {
|
133
145
|
getTaxa(): T[];
|
134
146
|
}
|
package/lib/index.js
CHANGED
@@ -6,6 +6,7 @@ import { Exceptions } from "./exceptions.js";
|
|
6
6
|
import { ExternalSites } from "./externalsites.js";
|
7
7
|
import { Families } from "./taxonomy/families.js";
|
8
8
|
import { Files } from "./files.js";
|
9
|
+
import { GenericPage } from "./web/pageGeneric.js";
|
9
10
|
import { HTML } from "./html.js";
|
10
11
|
import { HTMLFragments } from "./utils/htmlFragments.js";
|
11
12
|
import { HTMLTaxon } from "./htmltaxon.js";
|
@@ -24,6 +25,7 @@ export {
|
|
24
25
|
ExternalSites,
|
25
26
|
Families,
|
26
27
|
Files,
|
28
|
+
GenericPage,
|
27
29
|
HTML,
|
28
30
|
HTMLFragments,
|
29
31
|
HTMLTaxon,
|
package/lib/photo.js
CHANGED
@@ -60,6 +60,15 @@ export class Photo {
|
|
60
60
|
* @returns {string} The URL to retrieve the image file.
|
61
61
|
*/
|
62
62
|
getUrl() {
|
63
|
-
return
|
63
|
+
return Photo.getUrl(this.#id, this.#ext);
|
64
|
+
}
|
65
|
+
|
66
|
+
/**
|
67
|
+
* @param {number|string} id
|
68
|
+
* @param {string} ext
|
69
|
+
* @returns {string} The URL to retrieve the image file.
|
70
|
+
*/
|
71
|
+
static getUrl(id, ext) {
|
72
|
+
return `https://inaturalist-open-data.s3.amazonaws.com/photos/${id}/medium.${ext}`;
|
64
73
|
}
|
65
74
|
}
|
package/lib/tools/inat.js
CHANGED
@@ -3,6 +3,7 @@ import { Files } from "../files.js";
|
|
3
3
|
import { CSV } from "../csv.js";
|
4
4
|
import { sleep } from "../util.js";
|
5
5
|
import { TaxaCSV } from "./taxacsv.js";
|
6
|
+
import { SynCSV } from "./syncsv.js";
|
6
7
|
|
7
8
|
/**
|
8
9
|
* @typedef {{id:string,
|
@@ -65,8 +66,10 @@ export class INat {
|
|
65
66
|
|
66
67
|
const missingTaxa = [];
|
67
68
|
|
68
|
-
|
69
|
+
/** @type {Map<string,string>} */
|
69
70
|
const idsToUpdate = new Map();
|
71
|
+
/** @type {import("./syncsv.js").SynData[]} */
|
72
|
+
const synonymsToAdd = [];
|
70
73
|
|
71
74
|
for (const taxon of taxa.getTaxonList()) {
|
72
75
|
const name = taxon.getName();
|
@@ -103,12 +106,14 @@ export class INat {
|
|
103
106
|
errorLog,
|
104
107
|
data.name,
|
105
108
|
data.iNatName,
|
109
|
+
synonymsToAdd,
|
106
110
|
);
|
107
111
|
}
|
108
112
|
|
109
113
|
this.#checkExceptions(taxa, exceptions, errorLog);
|
110
114
|
|
111
115
|
if (update) {
|
116
|
+
updateSynCSV(dataDir, synonymsToAdd);
|
112
117
|
updateTaxaCSV(dataDir, idsToUpdate);
|
113
118
|
}
|
114
119
|
}
|
@@ -167,14 +172,21 @@ export class INat {
|
|
167
172
|
}
|
168
173
|
|
169
174
|
/**
|
170
|
-
*
|
171
175
|
* @param {import("../types.js").Taxa} taxa
|
172
176
|
* @param {import("../exceptions.js").Exceptions} exceptions
|
173
177
|
* @param {import("../errorlog.js").ErrorLog} errorLog
|
174
178
|
* @param {string} name
|
175
179
|
* @param {string} iNatName
|
180
|
+
* @param {import("./syncsv.js").SynData[]} synonymsToAdd
|
176
181
|
*/
|
177
|
-
static async #findCurrentName(
|
182
|
+
static async #findCurrentName(
|
183
|
+
taxa,
|
184
|
+
exceptions,
|
185
|
+
errorLog,
|
186
|
+
name,
|
187
|
+
iNatName,
|
188
|
+
synonymsToAdd,
|
189
|
+
) {
|
178
190
|
/**
|
179
191
|
* @param {{matched_term:string,name:string,rank:string}[]} results
|
180
192
|
* @param {string} iNatName
|
@@ -242,11 +254,17 @@ export class INat {
|
|
242
254
|
}
|
243
255
|
}
|
244
256
|
} else {
|
257
|
+
const formerName = this.makeSynonymName(result, errorLog);
|
245
258
|
errorLog.log(
|
246
259
|
name,
|
247
260
|
"found iNat synonym",
|
248
|
-
|
261
|
+
formerName + "," + name + ",INAT",
|
249
262
|
);
|
263
|
+
synonymsToAdd.push({
|
264
|
+
Former: formerName,
|
265
|
+
Current: name,
|
266
|
+
Type: "INAT",
|
267
|
+
});
|
250
268
|
}
|
251
269
|
|
252
270
|
// Delay to throttle queries to iNat API.
|
@@ -297,6 +315,17 @@ class InatTaxon {
|
|
297
315
|
}
|
298
316
|
}
|
299
317
|
|
318
|
+
/**
|
319
|
+
* @param {string} dataDir
|
320
|
+
* @param {import("./syncsv.js").SynData[]} synonymsToAdd
|
321
|
+
*/
|
322
|
+
function updateSynCSV(dataDir, synonymsToAdd) {
|
323
|
+
const csv = new SynCSV(dataDir);
|
324
|
+
const data = csv.getData();
|
325
|
+
data.push(...synonymsToAdd);
|
326
|
+
csv.write();
|
327
|
+
}
|
328
|
+
|
300
329
|
/**
|
301
330
|
* @param {string} dataDir
|
302
331
|
* @param {Map<string,string>} idsToUpdate
|
package/lib/util.js
CHANGED
@@ -1,16 +1,19 @@
|
|
1
1
|
/**
|
2
2
|
* Break an array into chunks of a desired size
|
3
3
|
* https://github.com/you-dont-need/You-Dont-Need-Lodash-Underscore?tab=readme-ov-file#_chunk
|
4
|
-
* @
|
4
|
+
* @template T
|
5
|
+
* @param {T[]} input
|
5
6
|
* @param {number} size
|
6
|
-
* @returns {
|
7
|
+
* @returns {T[][]}
|
7
8
|
*/
|
8
9
|
export function chunk(input, size) {
|
10
|
+
/** @type {T[][]} */
|
11
|
+
const result = [];
|
9
12
|
return input.reduce((arr, item, idx) => {
|
10
13
|
return idx % size === 0
|
11
14
|
? [...arr, [item]]
|
12
15
|
: [...arr.slice(0, -1), [...arr.slice(-1)[0], item]];
|
13
|
-
},
|
16
|
+
}, result);
|
14
17
|
}
|
15
18
|
|
16
19
|
/**
|
package/lib/utils/inat-tools.js
CHANGED
@@ -41,11 +41,10 @@ import { chunk, sleep } from "../util.js";
|
|
41
41
|
const ALLOWED_LICENSE_CODES = ["cc0", "cc-by", "cc-by-nc"];
|
42
42
|
|
43
43
|
/**
|
44
|
-
* @param {
|
44
|
+
* @param {string[]} inatTaxonIDs
|
45
45
|
* @return {Promise<InatApiTaxon[]>}
|
46
46
|
*/
|
47
|
-
async function fetchInatTaxa(
|
48
|
-
const inatTaxonIDs = taxa.map((taxon) => taxon.getINatID()).filter(Boolean);
|
47
|
+
async function fetchInatTaxa(inatTaxonIDs) {
|
49
48
|
const url = `https://api.inaturalist.org/v2/taxa/${inatTaxonIDs.join(",")}?fields=(taxon_photos:(photo:(medium_url:!t,attribution:!t,license_code:!t)))`;
|
50
49
|
const resp = await fetch(url);
|
51
50
|
if (!resp.ok) {
|
@@ -77,13 +76,11 @@ export async function getTaxonPhotos(taxaToUpdate) {
|
|
77
76
|
let taxaRetrieved = 0;
|
78
77
|
|
79
78
|
for (const batch of chunk(taxaToUpdate, 30)) {
|
80
|
-
const inatTaxa = await fetchInatTaxa(batch);
|
79
|
+
const inatTaxa = await fetchInatTaxa(batch.map((t) => t.getINatID()));
|
81
80
|
for (const iNatTaxon of inatTaxa) {
|
82
|
-
const iNatTaxonPhotos = iNatTaxon.taxon_photos
|
83
|
-
.
|
84
|
-
|
85
|
-
)
|
86
|
-
.slice(0, 5);
|
81
|
+
const iNatTaxonPhotos = iNatTaxon.taxon_photos.filter((tp) =>
|
82
|
+
ALLOWED_LICENSE_CODES.includes(tp.photo.license_code),
|
83
|
+
);
|
87
84
|
|
88
85
|
const taxonName = idMap.get(iNatTaxon.id.toString());
|
89
86
|
if (!taxonName) {
|
package/lib/web/pageGeneric.js
CHANGED