@ca-plant-list/ca-plant-list 0.4.31 → 0.4.33
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/inatobsphotos.csv +9341 -9281
- package/data/inattaxonphotos.csv +616 -177
- package/data/synonyms.csv +4 -1
- package/data/taxa.csv +31 -28
- package/data/text/Brodiaea-elegans-subsp-elegans.md +1 -0
- package/data/text/Brodiaea-terrestris-subsp-terrestris.md +1 -0
- package/lib/ebook/ebooksitegenerator.js +2 -0
- package/lib/photo.js +6 -4
- package/lib/taxonomy/taxa.js +3 -22
- package/lib/utils/inat-tools.js +211 -43
- package/package.json +1 -1
- package/scripts/cpl-photos.js +334 -62
package/data/synonyms.csv
CHANGED
@@ -686,6 +686,7 @@ Erigeron peregrinus var. callianthemus,Erigeron glacialis var. glacialis
|
|
686
686
|
Eriocoma lemmonii subsp. lemmonii,Stipa lemmonii var. lemmonii,INAT
|
687
687
|
Eriogonella membranacea,Chorizanthe membranacea
|
688
688
|
Eriogonum acetoselloides,Eriogonum gracile var. gracile
|
689
|
+
Eriogonum arachnoideum,Eriogonum latifolium
|
689
690
|
Eriogonum auriculatum,Eriogonum nudum var. auriculatum
|
690
691
|
Eriogonum caninum,Eriogonum luteolum var. caninum
|
691
692
|
Eriogonum curvatum,Eriogonum wrightii var. subscaposum
|
@@ -706,6 +707,7 @@ Eriogonum lobbii var. minus,Eriogonum lobbii
|
|
706
707
|
Eriogonum marifolium var. incanum,Eriogonum incanum
|
707
708
|
Eriogonum nivale,Eriogonum ovalifolium var. nivale
|
708
709
|
Eriogonum nudum subsp. pauciflorum,Eriogonum nudum var. pauciflorum
|
710
|
+
Eriogonum oblongifolium,Eriogonum latifolium
|
709
711
|
Eriogonum polifolium,Eriogonum fasciculatum var. polifolium
|
710
712
|
Eriogonum polyanthum var. bahiiforme,Eriogonum umbellatum var. bahiiforme
|
711
713
|
Eriogonum revolutum,Eriogonum fasciculatum var. polifolium
|
@@ -1096,6 +1098,7 @@ Linum micranthum,Hesperolinon micranthum
|
|
1096
1098
|
Linum perenne subsp. lewisii,Linum lewisii var. lewisii
|
1097
1099
|
Lippia lanceolata,Phyla lanceolata
|
1098
1100
|
Lithophragma affine subsp. mixtum,Lithophragma affine
|
1101
|
+
Lithophragma parviflorum,Lithophragma parviflorum var. parviflorum,INAT
|
1099
1102
|
Lithophragma scabrella,Lithophragma bolanderi
|
1100
1103
|
Lithophragma tripartitum,Lithophragma affine
|
1101
1104
|
Lolium arundinaceum,Festuca arundinacea,INAT
|
@@ -1152,7 +1155,6 @@ Lupinus congdonii,Lupinus bicolor
|
|
1152
1155
|
Lupinus densiflorus,Lupinus microcarpus var. densiflorus
|
1153
1156
|
Lupinus densiflorus var. aureus,Lupinus microcarpus var. densiflorus
|
1154
1157
|
Lupinus densiflorus var. austrocollium,Lupinus microcarpus var. microcarpus
|
1155
|
-
Lupinus densiflorus var. densiflorus,Lupinus microcarpus var. densiflorus,INAT
|
1156
1158
|
Lupinus densiflorus var. palustris,Lupinus microcarpus var. microcarpus
|
1157
1159
|
Lupinus densiflorus var. persecundus,Lupinus microcarpus var. microcarpus
|
1158
1160
|
Lupinus nanus subsp. apricus,Lupinus nanus
|
@@ -1689,6 +1691,7 @@ Quercus lobata f. rarita,Quercus lobata
|
|
1689
1691
|
Quercus lobata var. lobata,Quercus lobata
|
1690
1692
|
Quercus lobata var. turbinata,Quercus lobata
|
1691
1693
|
Quercus lobata var. walteri,Quercus lobata
|
1694
|
+
Ranunculus aquatilis,Ranunculus aquatilis var. aquatilis,INAT
|
1692
1695
|
Ranunculus aquatilis var. capillaceus,Ranunculus aquatilis var. diffusus
|
1693
1696
|
Ranunculus aquatilis var. harrisii,Ranunculus aquatilis var. diffusus
|
1694
1697
|
Ranunculus aquatilis var. hispidulus,Ranunculus aquatilis var. aquatilis
|
package/data/taxa.csv
CHANGED
@@ -37,11 +37,12 @@ Aegilops triuncialis,barbed goatgrass,X,11997,107,60272,134011,true
|
|
37
37
|
Aesculus californica,California buckeye,N,12026,111,53348,9383,,California Buckeye,,white
|
38
38
|
Ageratina adenophora,,X,735,117,64114,201878,true
|
39
39
|
Agoseris apargioides var. apargioides,,N,6310,122,58797,201889,true,Coast Dandelion
|
40
|
-
Agoseris grandiflora var. grandiflora,
|
41
|
-
Agoseris heterophylla var. cryptopleura,
|
42
|
-
Agoseris heterophylla var. heterophylla
|
40
|
+
Agoseris grandiflora var. grandiflora,big mountain dandelion,N,79217,10490,80331,201900,true,Giant Mountain Dandelion,perennial,yellow,4,7
|
41
|
+
Agoseris heterophylla var. cryptopleura,mountain dandelion,N,6324,10758,80333,206485,true,,annual,yellow,5,6
|
42
|
+
Agoseris heterophylla var. heterophylla,annual false dandelion,N,6323,9654,56859,206486,true,,annual,yellow,5,6
|
43
43
|
Agoseris heterophylla,,N,794,132,56856,201901,true,Annual Agoseris
|
44
|
-
Agoseris
|
44
|
+
Agoseris hirsuta,Coast Range agoseris,N,777,10491,61169,206487,true,Coast Range Agoseris,perennial,yellow,4,6
|
45
|
+
Agoseris retrorsa,spearleaf agoseris,N,797,133,57908,201902,true,Spearleaf Agoseris,perennial,yellow,4,8
|
45
46
|
Agrostis avenacea,blown grass,X,12268,138,288066,134238,Lachnagrostis filiformis
|
46
47
|
Agrostis capillaris,colonial bent,X,12276,140,75312,134268,true
|
47
48
|
Agrostis exarata,spike redtop,N,12284,143,59088,134320,true,Spike Bentgrass
|
@@ -237,7 +238,7 @@ Brickellia californica,California brickelbush,N,1794,1152,58803,202493,true,Bric
|
|
237
238
|
Briza maxima,quaking grass,X,16135,1165,57157,136989,true
|
238
239
|
Briza minor,little quaking grass,X,16137,1166,57162,136993,true
|
239
240
|
Brodiaea elegans subsp. elegans,harvest brodiaea,N,49461,1175,57127,202520,true,Harvest Brodiaea,corm,purple,4,8
|
240
|
-
Brodiaea terrestris subsp. terrestris,,N,49464,1188,59081,202533,true,Dwarf Brodiaea,corm
|
241
|
+
Brodiaea terrestris subsp. terrestris,,N,49464,1188,59081,202533,true,Dwarf Brodiaea,corm,purple,4,7
|
241
242
|
Bromus arenarius,Australian chess,X,16216,1191,58368,137101,true
|
242
243
|
Bromus berteroanus,Chilean chess,X,16219,9740,75897,137110,true
|
243
244
|
Bromus catharticus var. catharticus,rescue grass,X,85279,11433,80494,169088,true
|
@@ -275,9 +276,9 @@ Callitriche marginata,water-starwort,N,16692,1259,58093,64128,true,Winged Water
|
|
275
276
|
Callitriche trochlearis,,N,16702,1261,69994,64137,true,Effluent Water-starwort
|
276
277
|
Calochortus albus,white globe lily,N,16710,1264,51315,202570,true,White Globe Lily,,white,4,6
|
277
278
|
Calochortus argillosus,clay mariposa lily,N,76542,1268,64330,202576,true,Clay Mariposa Lily,bulb,"white,pink",4,6
|
278
|
-
Calochortus clavatus var. pallidus
|
279
|
-
Calochortus invenustus
|
280
|
-
Calochortus leichtlinii
|
279
|
+
Calochortus clavatus var. pallidus,,N,55455,1275,58360,202583,true,Pale Yellow Mariposa
|
280
|
+
Calochortus invenustus,,N,16733,1284,64432,202590,true,Plain Mariposa Lily
|
281
|
+
Calochortus leichtlinii,,N,16735,1288,52542,202594,true,Leichtlin's Mariposa Lily
|
281
282
|
Calochortus luteus,yellow mariposa lily,N,16737,1290,51077,202595,true,Yellow Mariposa Lily,bulb,yellow,4,6
|
282
283
|
Calochortus pulchellus,Mt. Diablo fairy lantern,N,16755,1303,47326,202609,true,Mt. Diablo Fairy Lantern,bulb,,,,50,1B.2,,,S2,G2
|
283
284
|
Calochortus splendens,,N,16759,1306,58361,202611,true,Splendid Mariposa Lily,bulb
|
@@ -307,7 +308,7 @@ Camissoniopsis intermedia,,N,89203,11455,75982,12189,true,Intermediate Suncup
|
|
307
308
|
Camissoniopsis micrantha,,N,89206,11458,75985,12192,true,Small Flowered Evening Primrose
|
308
309
|
Capsella bursa-pastoris,shepherd's purse,X,17109,1472,56121,12296,true
|
309
310
|
Cardamine californica,"milkmaids,tooth wort",N,76456,1479,49651,12325,true,Milk Maids
|
310
|
-
Cardamine hirsuta
|
311
|
+
Cardamine hirsuta,hairy bittercress,X,17158,1486,55829,12347,true
|
311
312
|
Cardamine oligosperma,,N,17171,1493,52981,12369,true,Little Western Bittercress
|
312
313
|
Carduus pycnocephalus subsp. pycnocephalus,Italian thistle,X,82221,11464,79672,202704,true
|
313
314
|
Carduus tenuiflorus,,X,1902,1505,53007,202705,true
|
@@ -364,7 +365,7 @@ Ceanothus thyrsiflorus,blue blossom,N,18501,1830,56617,12790,true,Blueblossom Ce
|
|
364
365
|
Centaurea calcitrapa,purple star-thistle,X,1922,1845,56865,202776,true
|
365
366
|
Centaurea cyanus,"bachelor's button,cornflower",X,1925,1846,933404,202778,true
|
366
367
|
Centaurea iberica,Iberian starthistle,X,1931,1849,76212,206620,true
|
367
|
-
Centaurea melitensis,"tocalote,Maltese
|
368
|
+
Centaurea melitensis,"tocalote,Maltese star-thistle",X,1934,1851,57922,202785,true
|
368
369
|
Centaurea solstitialis,yellow star-thistle,X,1946,1853,52588,202793,true
|
369
370
|
Centranthus ruber,red valerian,X,18595,1864,57283,202799
|
370
371
|
Centromadia fitchii,Fitch's spikeweed,N,77593,10809,58804,202801,true,Fitch Spikeweed
|
@@ -443,7 +444,7 @@ Claytonia exigua subsp. glauca,,N,76949,2233,57229,127855,true,Serpentine Spring
|
|
443
444
|
Claytonia gypsophiloides,candy stripers,N,19620,2234,55344,112597,true,Gypsum Springbeauty,annual,pink,3,5
|
444
445
|
Claytonia parviflora subsp. parviflora,,N,74695,2242,58296,127861,true,Utah Miner's Lettuce,"annual,perennial",white,3,6
|
445
446
|
Claytonia perfoliata subsp. mexicana,,N,76956,2245,53004,127866,true,Southern Miner's Lettuce,annual,white,2,4
|
446
|
-
Claytonia perfoliata subsp. perfoliata,
|
447
|
+
Claytonia perfoliata subsp. perfoliata,miner's lettuce,N,49921,2246,53002,127867,true,Miner's Lettuce,annual,white,1,5
|
447
448
|
Claytonia rubra subsp. depressa,red stem spring beauty,N,76957,2248,79751,127869,true,Miner's Lettuce,annual,white,2,4
|
448
449
|
Clematis lasiantha,pipestems,N,19682,2254,49876,13749,true,Chaparral Clematis,,white,1,6
|
449
450
|
Clematis ligusticifolia,virgin's bower,N,19683,2255,58996,13750,true,Virgin's Bower,,white,6,9
|
@@ -701,6 +702,7 @@ Eriogonum fasciculatum var. polifolium,,N,58546,3247,58272,129983,true,Interior
|
|
701
702
|
Eriogonum gracile var. gracile,slender buckwheat,N,58563,3257,58982,129999,true,Slender Woolly Wild Buckwheat
|
702
703
|
Eriogonum incanum,frosted buckwheat,N,24809,3279,62680,114044,true,Frosted Wild Buckwheat,perennial,yellow,6,9
|
703
704
|
Eriogonum inerme var. inerme,unarmed buckwheat,N,58586,9110,80799,130026,true,Unarmed Wild Buckwheat
|
705
|
+
Eriogonum latifolium,seaside wild buckwheat,N,24832,3293,57211,114060,true,Coast Buckwheat,perennial,"white,pink",1,12
|
704
706
|
Eriogonum lobbii,granite buckwheat,N,24839,9935,77005,114067,true,Lobb's Buckwheat,perennial,"white,pink",6,8
|
705
707
|
Eriogonum luteolum var. caninum,Tiburon buckwheat,N,58623,3297,61205,130067,true,Tiburon Buckwheat,,,,,733,1B.2,,,S2,G5T2
|
706
708
|
Eriogonum luteolum var. luteolum,,N,58624,3298,64226,130068,true,Golden-carpet Wild Buckwheat
|
@@ -835,7 +837,7 @@ Geranium molle,cranes-bill geranium,X,26729,3800,53076,22359
|
|
835
837
|
Geranium potentilloides,,X,26735,3802,77258,22368
|
836
838
|
Geranium purpureum,little robin,X,77514,8594,57643,22370
|
837
839
|
Geranium robertianum,,X,26742,3806,55925,22376
|
838
|
-
Geranium rotundifolium
|
840
|
+
Geranium rotundifolium,round-leaved crane's-bill,X,26743,3808,77261,22377
|
839
841
|
Gilia achilleifolia subsp. achilleifolia,,N,50744,3813,51101,95568,,California Gilia
|
840
842
|
Gilia achilleifolia subsp. multicaulis,,N,50745,3814,51102,95570,,California Gilia
|
841
843
|
Gilia capitata subsp. capitata,,N,50764,3834,57798,95588,,Bluehead Gilia
|
@@ -894,7 +896,7 @@ Heterotheca grandiflora,telegraph weed,N,3419,4142,58831,204286,true,Telegraph W
|
|
894
896
|
Heterotheca oregona var. rudis,Oregon goldenaster,N,7614,4146,,206910,true
|
895
897
|
Heterotheca oregona var. scaberrima,goldenaster,N,7616,4147,80937,204289,true
|
896
898
|
Heterotheca sessiliflora subsp. bolanderi,Bolander's goldenaster,N,74443,4151,61170,204291,true,Bolander's Goldenaster
|
897
|
-
Heterotheca sessiliflora subsp. echioides
|
899
|
+
Heterotheca sessiliflora subsp. echioides,bristly goldenaster,N,74445,4152,58834,204292,true,Bristly Goldenaster
|
898
900
|
Heuchera micrantha,alumroot,N,28126,4171,51274,22981,true,Crevice Alumroot
|
899
901
|
Hibiscus lasiocarpos var. occidentalis,rose-mallow,N,91751,11194,80940,266649,,Rose Mallow,,,,,906,1B.2,,,S3,G5T3
|
900
902
|
Hieracium albiflorum,white hawkweed,N,3471,4184,63937,204303,true,White Hawkweed
|
@@ -920,7 +922,7 @@ Hordeum murinum subsp. leporinum,,X,50971,4227,52795,204330,true
|
|
920
922
|
Hordeum murinum subsp. murinum,,X,50972,4228,63333,167373,true
|
921
923
|
Horkelia californica var. californica,California horkelia,N,81857,11731,237030,48514,true,California Horkelia
|
922
924
|
Horkelia californica var. elata,tall horkelia,N,81856,11732,80945,48515,true,California Honeydew
|
923
|
-
Horkelia californica var. frondosa,leafy horkelia,N,81858,11733
|
925
|
+
Horkelia californica var. frondosa,leafy horkelia,N,81858,11733,1629357,48516,true,California Horkelia
|
924
926
|
Horkelia cuneata var. sericea,Kellogg's horkelia,N,81861,11738,80951,48524,true,Kellogg's Horkelia,,,,,910,1B.1,,,S1?,G4T1?
|
925
927
|
Hornungia procumbens,,X,81333,11748,77452,23250,true
|
926
928
|
Hosackia crassifolia var. crassifolia,,N,91757,11750,80960,48541,,Big Deervetch
|
@@ -1082,17 +1084,17 @@ Lithophragma affine,woodland star,N,31236,4922,50803,24525,true,San Francisco Wo
|
|
1082
1084
|
Lithophragma bolanderi,,N,31238,4923,77787,24527,true,Bolander's Woodland Star,perennial,white,2,7
|
1083
1085
|
Lithophragma cymbalaria,,N,31241,4925,58323,24531,true,Mission Woodland Star,perennial,white,3,5
|
1084
1086
|
Lithophragma heterophyllum,hill starflower,N,31244,4927,53124,24533,true,Hillside Woodland Star,perennial,white,2,6
|
1085
|
-
Lithophragma parviflorum var. parviflorum,,N,60952,4930,
|
1087
|
+
Lithophragma parviflorum var. parviflorum,,N,60952,4930,77791,48936,Lithophragma parviflorum,Smallflower Woodland-star,perennial,"white,pink",3,7
|
1086
1088
|
Lobularia maritima,sweet alyssum,X,31347,4939,56992,24547,true
|
1087
1089
|
Logfia filaginoides,herba impia,N,81847,10960,64254,204735,true,California Cottonrose,annual,red,2,5
|
1088
1090
|
Logfia gallica,,X,31363,9521,56949,204736,true,,annual,"brown,yellow",3,7
|
1089
1091
|
Lomatium californicum,chu-chu-pate,N,31389,4950,56824,204737,,California Lomatium,,yellow,4,6
|
1090
1092
|
Lomatium caruifolium var. caruifolium,,N,61026,4953,64328,204739,,Alkali Desertparsley
|
1091
1093
|
Lomatium caruifolium var. denticulatum,,N,61027,4954,60123,204740
|
1092
|
-
Lomatium dasycarpum subsp. dasycarpum,,N,51334,4960,56829,204743,,Woolly Fruited Lomatium
|
1093
|
-
Lomatium macrocarpum,,N,31433,4974,56826,204754,,Bigseed Biscuitroot
|
1094
|
+
Lomatium dasycarpum subsp. dasycarpum,,N,51334,4960,56829,204743,,Woolly Fruited Lomatium,perennial,white,3,6
|
1095
|
+
Lomatium macrocarpum,,N,31433,4974,56826,204754,,Bigseed Biscuitroot,perennial,white,4,6
|
1094
1096
|
Lomatium nudicaule,,N,31441,4983,77818,204759,,Barestem Biscuitroot
|
1095
|
-
Lomatium utriculatum,,N,31473,4999,56827,204766,,Common Lomatium
|
1097
|
+
Lomatium utriculatum,,N,31473,4999,56827,204766,,Common Lomatium,perennial,yellow,2,5
|
1096
1098
|
Lonicera hispidula,California honeysuckle,N,31505,10075,53416,204768,,Pink Honeysuckle,,pink,5,6
|
1097
1099
|
Lonicera interrupta,chaparral honeysuckle,N,31506,5009,60878,204770,,Chaparral Honeysuckle,,"yellow,white",4,5
|
1098
1100
|
Lonicera involucrata var. ledebourii,black twinberry,N,61081,5012,60990,204773,,Twinberry Honeysuckle
|
@@ -1117,7 +1119,7 @@ Lupinus latifolius var. dudleyi,,N,76660,5161,81063,49195,,Broadleaf Lupine
|
|
1117
1119
|
Lupinus latifolius var. latifolius,"broadleaf lupine,wood lupine",N,75772,5162,64242,204794,,Broad Leaf Lupine,perennial,blue,4,9
|
1118
1120
|
Lupinus littoralis var. variicolor,,N,80538,14304,,49237
|
1119
1121
|
Lupinus luteolus,butter lupine,N,31956,5176,77875,25020,,Pale Yellow Lupine
|
1120
|
-
Lupinus microcarpus var. densiflorus,,N,76668,5184,
|
1122
|
+
Lupinus microcarpus var. densiflorus,,N,76668,5184,57058,49248,,Dense Flowered Platycarpos,annual,"yellow,white",4,6
|
1121
1123
|
Lupinus microcarpus var. microcarpus,chick lupine,N,75776,5186,58159,204796,,Valley Lupine,annual,"pink,blue",3,6
|
1122
1124
|
Lupinus nanus,sky lupine,N,31978,5189,50612,25068,,Sky Lupine,,blue,3,6
|
1123
1125
|
Lupinus pachylobus,big pod lupine,N,31998,5196,50613,25085,,Big Pod Lupine
|
@@ -1196,8 +1198,8 @@ Mercurialis annua,annual mercury,X,33300,5450,68838,25939,true
|
|
1196
1198
|
Mesembryanthemum nodiflorum,,X,33363,5461,71191,116177,true
|
1197
1199
|
Micranthes californica,,N,91942,11841,78023,26022,true,California Saxifrage
|
1198
1200
|
Micropus amphibolus,,N,33474,5462,78034,204946,true,Mount Diablo Cottonseed,,,,,1507,3.2,,,S3S4,G3G4
|
1199
|
-
Micropus californicus var. californicus,slender cottonweed,N,7993,5464,58030,204950,true,Cottontop
|
1200
|
-
Micropus californicus var. subvestitus
|
1201
|
+
Micropus californicus var. californicus,slender cottonweed,N,7993,5464,58030,204950,true,Cottontop,annual,white,3,6
|
1202
|
+
Micropus californicus var. subvestitus,Q-tips,N,7994,5465,81107,207081,true,Q-tips,annual,white,4,6
|
1201
1203
|
Microseris acuminata,,N,4146,5466,60142,204953,true,Sierra Foothill Silverpuffs
|
1202
1204
|
Microseris bigelovii,,N,4147,5467,60977,204954,true,Coastal Silverpuffs
|
1203
1205
|
Microseris campestris,,N,4149,5469,58031,204955,true,San Joaquin Silverpuffs
|
@@ -1517,7 +1519,7 @@ Quercus parvula var. shrevei,,N,76907,7005,81314,50722,,Shreve Oak
|
|
1517
1519
|
Quercus wislizeni var. frutescens,,N,64776,7011,58914,50761,,Interior Live Oak
|
1518
1520
|
Quercus wislizeni var. wislizeni,interior live oak,N,64777,7012,58915,50762,Quercus wislizeni,Interior Live Oak
|
1519
1521
|
Rafinesquia californica,California chicory,N,4548,7013,53193,205716,true,California Plumseed
|
1520
|
-
Ranunculus aquatilis var. aquatilis,,N,73500,10676,
|
1522
|
+
Ranunculus aquatilis var. aquatilis,,N,73500,10676,78817,50797,true
|
1521
1523
|
Ranunculus aquatilis var. diffusus,water buttercup,N,80687,11969,486980,50802,true,Hairleaf Water Buttercup
|
1522
1524
|
Ranunculus californicus,California buttercup,N,40867,7032,52173,29565,true,California Buttercup,,yellow
|
1523
1525
|
Ranunculus canus var. canus,,N,73506,11972,81324,50832,true
|
@@ -1534,7 +1536,7 @@ Raphanus raphanistrum,jointed charlock,X,40991,7063,55411,29701,true
|
|
1534
1536
|
Raphanus sativus,wild radish,X,40992,7064,995125,29702,true
|
1535
1537
|
Ravenella exigua,chaparral harebell,N,84837,14672,1342787,412341,,,,,,,265,1B.2,,,S2,G2
|
1536
1538
|
Ravenella griffinii,,N,84836,14796,1342789,412342
|
1537
|
-
Ravenella sharsmithiae,,N,108392,14673,1342786,
|
1539
|
+
Ravenella sharsmithiae,,N,108392,14673,1342786,432222,,,,,,,267,1B.2,,,S1S2,G1G2
|
1538
1540
|
Rhamnus alaternus,Italian buckthorn,X,81104,9447,82856,29726,true
|
1539
1541
|
Rhamnus crocea,spiny redberry,N,41069,7075,57243,29736,true,Spiny Redberry
|
1540
1542
|
Rhamnus ilicifolia,holly-leaf redberry,N,41070,7076,58308,29743,true,Hollyleaf Redberry
|
@@ -1562,6 +1564,7 @@ Rosa californica,California rose,N,41631,7179,53437,30158,true,California Wildro
|
|
1562
1564
|
Rosa gymnocarpa,wood rose,N,41652,7182,53441,30212,true,Wood Rose,,pink
|
1563
1565
|
Rosa rubiginosa,sweet-brier,X,41648,11057,78886,30313,true
|
1564
1566
|
Rosa spithamea,ground rose,N,41696,7187,53442,30338,true,Ground Rose
|
1567
|
+
Rosa woodsii,Woods' rose,N,41704,,78887,30370,true,Woods' Rose,perennial,pink
|
1565
1568
|
Rubus armeniacus,Himalayan blackberry,X,87465,10319,61317,30459
|
1566
1569
|
Rubus laciniatus,cutleaf blackberry,X,42009,7195,78891,30698,true
|
1567
1570
|
Rubus parviflorus,thimbleberry,N,42108,7200,51646,30808,true,Thimbleberry
|
@@ -1715,12 +1718,12 @@ Spergularia villosa,hairy sand-spurrey,X,45096,7717,60173,119423,true
|
|
1715
1718
|
Spiranthes porrifolia,,N,45262,7740,50742,206062,true,Creamy Lady's Tresses
|
1716
1719
|
Spiranthes romanzoffiana,,N,45264,7741,50743,206063,true,Irish Lady's-tresses
|
1717
1720
|
Sporobolus airoides,alkali sacaton,N,45286,7744,60313,149026,true,Alkali Sacaton
|
1718
|
-
Sporobolus alterniflorus,saltwater cordgrass,X,82872,7699,772903,
|
1719
|
-
Sporobolus densiflorus,dense-flowered cordgrass,X,108405,7700,169300,
|
1721
|
+
Sporobolus alterniflorus,saltwater cordgrass,X,82872,7699,772903,432224,Spartina alterniflora
|
1722
|
+
Sporobolus densiflorus,dense-flowered cordgrass,X,108405,7700,169300,432225,Spartina densiflora
|
1720
1723
|
Sporobolus foliosus,California cordgrass,N,82873,7701,772992,208714,Spartina foliosa
|
1721
1724
|
Sporobolus indicus,smutgrass,X,45308,7749,79187,149101,true
|
1722
1725
|
Sporobolus niliacus,,X,82876,2440,773022,397065,Crypsis vaginiflora
|
1723
|
-
Sporobolus pumilus,saltmeadow cordgrass,X,82878,7703,773004,
|
1726
|
+
Sporobolus pumilus,saltmeadow cordgrass,X,82878,7703,773004,432226,Spartina patens
|
1724
1727
|
Sporobolus schoenoides,swamp grass,X,82879,2439,773038,397066,Crypsis schoenoides
|
1725
1728
|
Stachys ajugoides,hedge nettle,N,45338,7751,53295,77371,,Ajuga Hedge Nettle
|
1726
1729
|
Stachys albens,,N,45339,7754,58192,77372,,White Hedge Nettle
|
@@ -1833,8 +1836,8 @@ Triphysaria eriantha subsp. eriantha,butter-and-eggs,N,53279,8129,59332,96567,tr
|
|
1833
1836
|
Triphysaria pusilla,,N,47296,8133,57276,78297,true,Dwarf Owl's-clover
|
1834
1837
|
Triphysaria versicolor subsp. faucibarbata,,N,53281,8135,60737,206269,true,Triphysaria
|
1835
1838
|
Trisetum canescens,,N,47326,8139,1538928,150155,true,Tall False Oat
|
1836
|
-
Triteleia hyacinthina,white brodiaea,N,47356,8154,57132,206275,true,White Brodiaea
|
1837
|
-
Triteleia laxa,Ithuriel's spear,N,47359,8160,51085,206281,true,Ithuriel's Spear
|
1839
|
+
Triteleia hyacinthina,white brodiaea,N,47356,8154,57132,206275,true,White Brodiaea,corm,white,3,7
|
1840
|
+
Triteleia laxa,Ithuriel's spear,N,47359,8160,51085,206281,true,Ithuriel's Spear,corm,blue,3,6
|
1838
1841
|
Triteleia peduncularis,,N,47363,8164,57133,206284,true,Long Rayed Brodiaea
|
1839
1842
|
Tropaeolum majus,garden nasturtium,X,47408,8167,54329,34321,true
|
1840
1843
|
Tropidocarpum capparideum,caper-fruited tropidocarpum,N,47411,8168,79456,34327,true,Caper-fruited Tropidocarpum,annual,yellow,3,4,1255,1B.1,,,S1,G1
|
@@ -0,0 +1 @@
|
|
1
|
+
Base of flower funnel-shaped.
|
@@ -0,0 +1 @@
|
|
1
|
+
Base of flower bell-shaped.
|
@@ -1,6 +1,7 @@
|
|
1
1
|
import path from "node:path";
|
2
2
|
import { Files } from "../files.js";
|
3
3
|
import { SiteGenerator } from "../sitegenerator.js";
|
4
|
+
import { XHTML } from "./xhtml.js";
|
4
5
|
|
5
6
|
export class EBookSiteGenerator extends SiteGenerator {
|
6
7
|
#pageEnd() {
|
@@ -21,6 +22,7 @@ export class EBookSiteGenerator extends SiteGenerator {
|
|
21
22
|
"../".repeat(depth) +
|
22
23
|
'css/main.css" rel="stylesheet" />';
|
23
24
|
html += "</head><body>";
|
25
|
+
html += XHTML.wrap("h1", attributes.title);
|
24
26
|
return html;
|
25
27
|
}
|
26
28
|
|
package/lib/photo.js
CHANGED
@@ -10,10 +10,10 @@ export class Photo {
|
|
10
10
|
#rights;
|
11
11
|
|
12
12
|
/**
|
13
|
-
* @param {
|
13
|
+
* @param {string} id
|
14
14
|
* @param {string} ext
|
15
|
-
* @param {import("./utils/inat-tools.js").
|
16
|
-
* @param {string} rightsHolder
|
15
|
+
* @param {import("./utils/inat-tools.js").AllowedLicenseCode} licenseCode
|
16
|
+
* @param {string|undefined} rightsHolder
|
17
17
|
*/
|
18
18
|
constructor(id, ext, licenseCode, rightsHolder) {
|
19
19
|
this.#id = id;
|
@@ -22,7 +22,9 @@ export class Photo {
|
|
22
22
|
if (licenseCode === "cc0") this.#rights = "CC0";
|
23
23
|
else if (licenseCode === "cc-by") this.#rights = "CC BY";
|
24
24
|
else if (licenseCode === "cc-by-nc") this.#rights = "CC BY-NC";
|
25
|
-
else
|
25
|
+
else {
|
26
|
+
throw new Error(`unexpected license code ${licenseCode}`);
|
27
|
+
}
|
26
28
|
}
|
27
29
|
|
28
30
|
/**
|
package/lib/taxonomy/taxa.js
CHANGED
@@ -115,27 +115,7 @@ class Taxa {
|
|
115
115
|
#loadPhotosFromFile(dataDir, filename) {
|
116
116
|
if (!fs.existsSync(path.join(dataDir, filename))) return;
|
117
117
|
/** @type {import("../utils/inat-tools.js").InatCsvPhoto[]} */
|
118
|
-
const csvPhotos = CSV.
|
119
|
-
/** @type {import("../utils/inat-tools.js").InatLicenseCode} */
|
120
|
-
let licenseCode = "cc-by";
|
121
|
-
if (row.licenseCode === "cc-by-nc-sa") licenseCode = "cc-by-nc-sa";
|
122
|
-
else if (row.licenseCode === "cc-by-nc") licenseCode = "cc-by-nc";
|
123
|
-
else if (row.licenseCode === "cc-by-nc-nd")
|
124
|
-
licenseCode = "cc-by-nc-nd";
|
125
|
-
else if (row.licenseCode === "cc-by") licenseCode = "cc-by";
|
126
|
-
else if (row.licenseCode === "cc-by-sa") licenseCode = "cc-by-sa";
|
127
|
-
else if (row.licenseCode === "cc-by-nd") licenseCode = "cc-by-nd";
|
128
|
-
else if (row.licenseCode === "pd") licenseCode = "pd";
|
129
|
-
else if (row.licenseCode === "gdfl") licenseCode = "gdfl";
|
130
|
-
else if (row.licenseCode === "cc0") licenseCode = "cc0";
|
131
|
-
return {
|
132
|
-
attrName: row.attrName,
|
133
|
-
ext: row.ext,
|
134
|
-
id: Number(row.id),
|
135
|
-
licenseCode,
|
136
|
-
name: row.name,
|
137
|
-
};
|
138
|
-
});
|
118
|
+
const csvPhotos = CSV.readFile(path.join(dataDir, filename));
|
139
119
|
for (const csvPhoto of csvPhotos) {
|
140
120
|
const taxon = this.getTaxon(csvPhoto.name);
|
141
121
|
if (!taxon) {
|
@@ -156,8 +136,9 @@ class Taxa {
|
|
156
136
|
* @param {string} dataDir
|
157
137
|
*/
|
158
138
|
#loadInatPhotos(dataDir) {
|
159
|
-
|
139
|
+
// Prefer taxon photos, as they are somewhat curated.
|
160
140
|
this.#loadPhotosFromFile(dataDir, "inattaxonphotos.csv");
|
141
|
+
this.#loadPhotosFromFile("./data", "inatobsphotos.csv");
|
161
142
|
this.#loadPhotosFromFile(dataDir, "inatobsphotos.csv");
|
162
143
|
}
|
163
144
|
|
package/lib/utils/inat-tools.js
CHANGED
@@ -2,25 +2,25 @@ import { ProgressMeter } from "../progressmeter.js";
|
|
2
2
|
import { chunk, sleep } from "../util.js";
|
3
3
|
|
4
4
|
/**
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
| "cc0"} InatLicenseCode
|
5
|
+
* @typedef {"cc0" | "cc-by" | "cc-by-nc"} AllowedLicenseCode
|
6
|
+
* @typedef {{place_id?:string,project_id?:string}} ObsPhotoLocationOptions
|
7
|
+
@typedef {{
|
8
|
+
id:number,
|
9
|
+
observation_photos: {
|
10
|
+
photo: import("./inat-tools.js").InatApiPhoto;
|
11
|
+
}[];
|
12
|
+
}} InatApiObservationPhotos
|
14
13
|
@typedef {{
|
15
14
|
id: string;
|
15
|
+
obsId?:string;
|
16
16
|
ext: string;
|
17
|
-
licenseCode:
|
17
|
+
licenseCode: AllowedLicenseCode;
|
18
18
|
attrName: string | undefined;
|
19
19
|
}} InatPhotoInfo
|
20
20
|
@typedef {{
|
21
21
|
id: number;
|
22
22
|
attribution: string;
|
23
|
-
license_code:
|
23
|
+
license_code: string;
|
24
24
|
medium_url?: string;
|
25
25
|
url?: string;
|
26
26
|
}} InatApiPhoto
|
@@ -30,15 +30,38 @@ import { chunk, sleep } from "../util.js";
|
|
30
30
|
photo: InatApiPhoto;
|
31
31
|
}[];
|
32
32
|
}} InatApiTaxon
|
33
|
-
@typedef {{
|
34
|
-
name: string;
|
35
|
-
id: number;
|
36
|
-
ext: string;
|
37
|
-
licenseCode: InatLicenseCode;
|
38
|
-
attrName: string;
|
39
|
-
}} InatCsvPhoto
|
33
|
+
@typedef {{name: string;} & InatPhotoInfo} InatCsvPhoto
|
40
34
|
*/
|
35
|
+
|
41
36
|
const ALLOWED_LICENSE_CODES = ["cc0", "cc-by", "cc-by-nc"];
|
37
|
+
const FIELDS_OBS_PHOTO =
|
38
|
+
"(id:!t,observation_photos:(photo:(url:!t,attribution:!t,license_code:!t)))";
|
39
|
+
|
40
|
+
/**
|
41
|
+
* @param {InatApiPhoto} apiPhoto
|
42
|
+
* @returns {InatPhotoInfo|undefined}
|
43
|
+
*/
|
44
|
+
export function convertToCSVPhoto(apiPhoto) {
|
45
|
+
const licenseCode = getAllowedLicenseCode(apiPhoto.license_code);
|
46
|
+
if (licenseCode === undefined) {
|
47
|
+
return;
|
48
|
+
}
|
49
|
+
const url = apiPhoto.medium_url || apiPhoto.url;
|
50
|
+
if (!url) {
|
51
|
+
return;
|
52
|
+
}
|
53
|
+
const ext = url.split(".").at(-1);
|
54
|
+
if (!ext) {
|
55
|
+
return;
|
56
|
+
}
|
57
|
+
/** @type {InatPhotoInfo} */
|
58
|
+
return {
|
59
|
+
id: apiPhoto.id.toString(),
|
60
|
+
ext: ext,
|
61
|
+
licenseCode: licenseCode,
|
62
|
+
attrName: getAttribution(apiPhoto.attribution),
|
63
|
+
};
|
64
|
+
}
|
42
65
|
|
43
66
|
/**
|
44
67
|
* @param {string[]} inatTaxonIDs
|
@@ -55,6 +78,122 @@ async function fetchInatTaxa(inatTaxonIDs) {
|
|
55
78
|
return json.results;
|
56
79
|
}
|
57
80
|
|
81
|
+
/**
|
82
|
+
* @param {import("../types.js").Taxon} taxon
|
83
|
+
* @param {{place_id?:string,project_id?:string}} locationOptions
|
84
|
+
* @return {Promise<InatApiObservationPhotos[]|Error>}
|
85
|
+
*/
|
86
|
+
async function fetchObservationsForTaxon(
|
87
|
+
taxon,
|
88
|
+
locationOptions = { place_id: "14" },
|
89
|
+
) {
|
90
|
+
const inatTaxonId = taxon.getINatID();
|
91
|
+
if (!inatTaxonId) return [];
|
92
|
+
let url = new URL(
|
93
|
+
`https://api.inaturalist.org/v2/observations/?taxon_id=${inatTaxonId}` +
|
94
|
+
"&photo_license=" +
|
95
|
+
ALLOWED_LICENSE_CODES.join(",") +
|
96
|
+
"&order=desc" +
|
97
|
+
"&order_by=votes" +
|
98
|
+
"&per_page=5",
|
99
|
+
);
|
100
|
+
url.searchParams.set("fields", FIELDS_OBS_PHOTO);
|
101
|
+
if (locationOptions.place_id) {
|
102
|
+
url.searchParams.set("place_id", locationOptions.place_id);
|
103
|
+
}
|
104
|
+
if (locationOptions.project_id) {
|
105
|
+
url.searchParams.set("project_id", locationOptions.project_id);
|
106
|
+
}
|
107
|
+
const resp = await getResponse(url);
|
108
|
+
if (resp instanceof Error) {
|
109
|
+
return resp;
|
110
|
+
}
|
111
|
+
|
112
|
+
if (!resp.ok) {
|
113
|
+
return new Error(await resp.text());
|
114
|
+
}
|
115
|
+
const json = await resp.json();
|
116
|
+
return json.results;
|
117
|
+
}
|
118
|
+
|
119
|
+
/**
|
120
|
+
* @param {string[]} obsIds
|
121
|
+
* @returns {Promise<InatApiObservationPhotos[]|Error>}
|
122
|
+
*/
|
123
|
+
export async function getObsPhotosForIds(obsIds) {
|
124
|
+
let url = new URL("https://api.inaturalist.org/v2/observations/");
|
125
|
+
url.searchParams.set("fields", FIELDS_OBS_PHOTO);
|
126
|
+
url.searchParams.set("id", obsIds.join(","));
|
127
|
+
url.searchParams.set("per_page", obsIds.length.toString());
|
128
|
+
|
129
|
+
const resp = await getResponse(url);
|
130
|
+
if (resp instanceof Error) {
|
131
|
+
return resp;
|
132
|
+
}
|
133
|
+
|
134
|
+
if (!resp.ok) {
|
135
|
+
return new Error(await resp.text());
|
136
|
+
}
|
137
|
+
const json = await resp.json();
|
138
|
+
return json.results;
|
139
|
+
}
|
140
|
+
|
141
|
+
/**
|
142
|
+
* @param {import("../types.js").Taxon[]} taxaToUpdate
|
143
|
+
* @param {ObsPhotoLocationOptions|undefined} locationOptions
|
144
|
+
* @returns {Promise<Map<string,InatPhotoInfo[]>>}
|
145
|
+
*/
|
146
|
+
export async function getObsPhotosForTaxa(taxaToUpdate, locationOptions) {
|
147
|
+
/** @type {Map<string,InatPhotoInfo[]>} */
|
148
|
+
const photos = new Map();
|
149
|
+
|
150
|
+
const meter = new ProgressMeter(
|
151
|
+
"retrieving observation photos",
|
152
|
+
taxaToUpdate.length,
|
153
|
+
);
|
154
|
+
|
155
|
+
for (let index = 0; index < taxaToUpdate.length; index++) {
|
156
|
+
const taxon = taxaToUpdate[index];
|
157
|
+
const observations = await fetchObservationsForTaxon(
|
158
|
+
taxon,
|
159
|
+
locationOptions,
|
160
|
+
);
|
161
|
+
if (observations instanceof Error) {
|
162
|
+
console.error(observations.message);
|
163
|
+
continue;
|
164
|
+
}
|
165
|
+
|
166
|
+
// Just get the CC-licensed ones, 5 per taxon should be fine (max is 20 on iNat). Whether or not
|
167
|
+
const rawPhotoInfo = observations
|
168
|
+
.map((obs) =>
|
169
|
+
obs.observation_photos.map((op) => {
|
170
|
+
return { obsId: obs.id, ...op.photo };
|
171
|
+
}),
|
172
|
+
)
|
173
|
+
.flat();
|
174
|
+
|
175
|
+
/** @type {InatPhotoInfo[]} */
|
176
|
+
const processedPhotoInfo = [];
|
177
|
+
for (const photo of rawPhotoInfo) {
|
178
|
+
if (processedPhotoInfo.length >= 5) {
|
179
|
+
break;
|
180
|
+
}
|
181
|
+
const obj = convertToCSVPhoto(photo);
|
182
|
+
if (!obj) {
|
183
|
+
continue;
|
184
|
+
}
|
185
|
+
processedPhotoInfo.push(obj);
|
186
|
+
}
|
187
|
+
photos.set(taxon.getName(), processedPhotoInfo);
|
188
|
+
|
189
|
+
meter.update(index + 1);
|
190
|
+
}
|
191
|
+
|
192
|
+
meter.stop();
|
193
|
+
|
194
|
+
return photos;
|
195
|
+
}
|
196
|
+
|
58
197
|
/**
|
59
198
|
* @param {import("../types.js").Taxon[]} taxaToUpdate
|
60
199
|
* @returns {Promise<Map<string,InatPhotoInfo[]>>}
|
@@ -78,37 +217,17 @@ export async function getTaxonPhotos(taxaToUpdate) {
|
|
78
217
|
for (const batch of chunk(taxaToUpdate, 30)) {
|
79
218
|
const inatTaxa = await fetchInatTaxa(batch.map((t) => t.getINatID()));
|
80
219
|
for (const iNatTaxon of inatTaxa) {
|
81
|
-
const iNatTaxonPhotos = iNatTaxon.taxon_photos.filter((tp) =>
|
82
|
-
ALLOWED_LICENSE_CODES.includes(tp.photo.license_code),
|
83
|
-
);
|
84
|
-
|
85
220
|
const taxonName = idMap.get(iNatTaxon.id.toString());
|
86
221
|
if (!taxonName) {
|
87
222
|
throw new Error(`iNat id ${iNatTaxon.id} not found`);
|
88
223
|
}
|
89
224
|
/** @type {InatPhotoInfo[]} */
|
90
225
|
const taxonPhotos = [];
|
91
|
-
for (const taxonPhoto of
|
92
|
-
const
|
93
|
-
if (!
|
94
|
-
|
95
|
-
|
96
|
-
/** @type {InatPhotoInfo} */
|
97
|
-
const obj = {
|
98
|
-
id: taxonPhoto.photo.id.toString(),
|
99
|
-
ext: ext,
|
100
|
-
licenseCode: taxonPhoto.photo.license_code,
|
101
|
-
attrName:
|
102
|
-
// Photographers retain copyright for most CC licenses,
|
103
|
-
// except CC0, so attribution is a bit different
|
104
|
-
taxonPhoto.photo.attribution.match(
|
105
|
-
/\(c\) (.*?),/,
|
106
|
-
)?.[1] ||
|
107
|
-
taxonPhoto.photo.attribution.match(
|
108
|
-
/uploaded by (.*)/,
|
109
|
-
)?.[1],
|
110
|
-
};
|
111
|
-
|
226
|
+
for (const taxonPhoto of iNatTaxon.taxon_photos) {
|
227
|
+
const obj = convertToCSVPhoto(taxonPhoto.photo);
|
228
|
+
if (!obj) {
|
229
|
+
continue;
|
230
|
+
}
|
112
231
|
taxonPhotos.push(obj);
|
113
232
|
}
|
114
233
|
photos.set(taxonName, taxonPhotos);
|
@@ -122,3 +241,52 @@ export async function getTaxonPhotos(taxaToUpdate) {
|
|
122
241
|
|
123
242
|
return photos;
|
124
243
|
}
|
244
|
+
|
245
|
+
/**
|
246
|
+
* @param {string} licenseCode
|
247
|
+
* @returns {AllowedLicenseCode|undefined}
|
248
|
+
*/
|
249
|
+
function getAllowedLicenseCode(licenseCode) {
|
250
|
+
switch (licenseCode) {
|
251
|
+
case "cc0":
|
252
|
+
case "cc-by":
|
253
|
+
case "cc-by-nc":
|
254
|
+
return licenseCode;
|
255
|
+
}
|
256
|
+
}
|
257
|
+
|
258
|
+
/**
|
259
|
+
* @param {string} rawAttribution
|
260
|
+
* @returns {string|undefined}
|
261
|
+
*/
|
262
|
+
function getAttribution(rawAttribution) {
|
263
|
+
// Photographers retain copyright for most CC licenses,
|
264
|
+
// except CC0, so attribution is a bit different
|
265
|
+
return (
|
266
|
+
rawAttribution.match(/\(c\) (.*?),/)?.[1] ||
|
267
|
+
rawAttribution.match(/uploaded by (.*)/)?.[1]
|
268
|
+
);
|
269
|
+
}
|
270
|
+
|
271
|
+
let lastQueryTime = Date.now();
|
272
|
+
/**
|
273
|
+
* @param {URL} url
|
274
|
+
* @returns {Promise<Response|Error>}
|
275
|
+
*/
|
276
|
+
async function getResponse(url) {
|
277
|
+
// If less than one second since last query, delay.
|
278
|
+
const delayTime = 1050 - (Date.now() - lastQueryTime);
|
279
|
+
if (delayTime > 0) {
|
280
|
+
await sleep(delayTime);
|
281
|
+
}
|
282
|
+
|
283
|
+
try {
|
284
|
+
lastQueryTime = Date.now();
|
285
|
+
return await fetch(url);
|
286
|
+
} catch (error) {
|
287
|
+
if (error instanceof Error) {
|
288
|
+
return error;
|
289
|
+
}
|
290
|
+
throw error;
|
291
|
+
}
|
292
|
+
}
|