@ca-plant-list/ca-plant-list 0.4.19 → 0.4.20
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/genera.json +36 -32
- package/data/synonyms.csv +2 -0
- package/data/taxa.csv +6 -5
- package/data/text/Rumex-conglomeratus.md +1 -0
- package/data/text/Rumex-obtusifolius.md +1 -0
- package/data/text/Rumex-pulcher.md +1 -0
- package/data/text/Rumex-salicifolius.md +1 -0
- package/lib/ebook/glossarypages.js +3 -3
- package/lib/ebook/images.js +2 -2
- package/lib/ebook/pages/page_list_families.js +1 -1
- package/lib/ebook/pages/page_list_flower_color.js +2 -2
- package/lib/ebook/pages/page_list_flowers.js +8 -8
- package/lib/ebook/pages/tocpage.js +2 -2
- package/lib/ebook/plantbook.js +2 -2
- package/lib/externalsites.js +20 -15
- package/lib/families.js +1 -1
- package/lib/flowercolor.js +2 -2
- package/lib/genera.js +2 -2
- package/lib/index.d.ts +31 -1
- package/lib/photo.js +1 -10
- package/lib/plants/glossary.js +2 -4
- package/lib/program.js +10 -2
- package/lib/sitegenerator.js +13 -3
- package/lib/taxa.js +14 -10
- package/lib/taxon.js +6 -3
- package/lib/tools/calflora.js +41 -8
- package/lib/tools/calscape.js +4 -4
- package/lib/tools/inat.js +7 -7
- package/lib/tools/jepsoneflora.js +28 -4
- package/lib/tools/jepsonfamilies.js +102 -0
- package/lib/tools/rpi.js +5 -5
- package/lib/tools/supplementaltext.js +43 -0
- package/lib/tools/taxacsv.js +2 -2
- package/lib/utils/inat-tools.js +39 -2
- package/lib/web/glossarypages.js +6 -6
- package/package.json +6 -7
- package/scripts/cpl-photos.js +2 -2
- package/scripts/cpl-tools.js +11 -3
- package/scripts/inatobsphotos.js +10 -1
- package/scripts/inattaxonphotos.js +45 -43
- package/types/classes.d.ts +0 -205
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "@ca-plant-list/ca-plant-list",
|
3
|
-
"version": "0.4.
|
3
|
+
"version": "0.4.20",
|
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": {
|
@@ -14,8 +14,7 @@
|
|
14
14
|
"ebook",
|
15
15
|
"jekyll",
|
16
16
|
"lib",
|
17
|
-
"scripts"
|
18
|
-
"types"
|
17
|
+
"scripts"
|
19
18
|
],
|
20
19
|
"exports": {
|
21
20
|
".": "./lib/index.js"
|
@@ -36,7 +35,7 @@
|
|
36
35
|
"inatobsphotos": "scripts/inatobsphotos.js"
|
37
36
|
},
|
38
37
|
"dependencies": {
|
39
|
-
"@htmltools/scrape": "^0.1.
|
38
|
+
"@htmltools/scrape": "^0.1.1",
|
40
39
|
"archiver": "^5.3.1",
|
41
40
|
"cli-progress": "^3.12.0",
|
42
41
|
"commander": "^12.1.0",
|
@@ -54,11 +53,11 @@
|
|
54
53
|
"@types/cli-progress": "^3.11.6",
|
55
54
|
"@types/jest": "^29.5.14",
|
56
55
|
"@types/markdown-it": "^14.1.2",
|
57
|
-
"@types/node": "^22.10.
|
56
|
+
"@types/node": "^22.10.7",
|
58
57
|
"@types/unzipper": "^0.10.9",
|
59
|
-
"eslint": "^9.
|
58
|
+
"eslint": "^9.18.0",
|
60
59
|
"jest": "^29.7.0",
|
61
60
|
"prettier": "^3.4.2",
|
62
|
-
"typescript": "^5.7.
|
61
|
+
"typescript": "^5.7.3"
|
63
62
|
}
|
64
63
|
}
|
package/scripts/cpl-photos.js
CHANGED
@@ -129,7 +129,7 @@ async function getTaxa(options) {
|
|
129
129
|
}
|
130
130
|
|
131
131
|
/**
|
132
|
-
* @returns {Map<string,InatPhotoInfo[]>}
|
132
|
+
* @returns {Map<string,import("../lib/utils/inat-tools.js").InatPhotoInfo[]>}
|
133
133
|
*/
|
134
134
|
function readPhotos() {
|
135
135
|
const photosFileName = `./data/${PHOTO_FILE_NAME}`;
|
@@ -140,7 +140,7 @@ function readPhotos() {
|
|
140
140
|
/** @type {Map<string,{id:string,ext:string,licenseCode:string,attrName:string}[]>} */
|
141
141
|
const taxonPhotos = new Map();
|
142
142
|
|
143
|
-
/** @type {InatCsvPhoto[]} */
|
143
|
+
/** @type {import("../lib/utils/inat-tools.js").InatCsvPhoto[]} */
|
144
144
|
// @ts-ignore
|
145
145
|
const csvPhotos = CSV.readFile(photosFileName);
|
146
146
|
for (const csvPhoto of csvPhotos) {
|
package/scripts/cpl-tools.js
CHANGED
@@ -11,6 +11,8 @@ import { JepsonEFlora } from "../lib/tools/jepsoneflora.js";
|
|
11
11
|
import { RPI } from "../lib/tools/rpi.js";
|
12
12
|
import { Config } from "../lib/config.js";
|
13
13
|
import { Taxa } from "../lib/taxa.js";
|
14
|
+
import { SupplementalText } from "../lib/tools/supplementaltext.js";
|
15
|
+
import { JepsonFamilies } from "../lib/tools/jepsonfamilies.js";
|
14
16
|
|
15
17
|
const TOOLS = {
|
16
18
|
CALFLORA: "calflora",
|
@@ -58,9 +60,11 @@ async function build(program, options) {
|
|
58
60
|
case TOOLS.CALFLORA:
|
59
61
|
await Calflora.analyze(
|
60
62
|
TOOLS_DATA_DIR,
|
63
|
+
options.datadir,
|
61
64
|
taxa,
|
62
65
|
exceptions,
|
63
66
|
errorLog,
|
67
|
+
!!options.update,
|
64
68
|
);
|
65
69
|
break;
|
66
70
|
case TOOLS.CALSCAPE:
|
@@ -86,11 +90,15 @@ async function build(program, options) {
|
|
86
90
|
break;
|
87
91
|
case TOOLS.JEPSON_EFLORA: {
|
88
92
|
const eflora = new JepsonEFlora(TOOLS_DATA_DIR, taxa, errorLog);
|
89
|
-
await eflora.analyze(
|
93
|
+
await eflora.analyze(
|
94
|
+
options.datadir,
|
95
|
+
exceptions,
|
96
|
+
!!options.update,
|
97
|
+
);
|
90
98
|
break;
|
91
99
|
}
|
92
100
|
case TOOLS.JEPSON_FAM:
|
93
|
-
|
101
|
+
await JepsonFamilies.build(TOOLS_DATA_DIR, "./data");
|
94
102
|
break;
|
95
103
|
case TOOLS.RPI:
|
96
104
|
await RPI.analyze(
|
@@ -102,7 +110,7 @@ async function build(program, options) {
|
|
102
110
|
);
|
103
111
|
break;
|
104
112
|
case TOOLS.TEXT:
|
105
|
-
|
113
|
+
SupplementalText.analyze(taxa, errorLog);
|
106
114
|
break;
|
107
115
|
default:
|
108
116
|
console.log("unrecognized tool: " + tool);
|
package/scripts/inatobsphotos.js
CHANGED
@@ -9,6 +9,15 @@ import { Program } from "../lib/program.js";
|
|
9
9
|
import { Taxa } from "../lib/taxa.js";
|
10
10
|
import { sleep } from "../lib/util.js";
|
11
11
|
|
12
|
+
/**
|
13
|
+
* @typedef {{
|
14
|
+
observation_photos: {
|
15
|
+
photo: import("../lib/utils/inat-tools.js").InatApiPhoto;
|
16
|
+
}[];
|
17
|
+
}} InatApiObservation
|
18
|
+
@typedef {import("../lib/program.js").CommandLineOptions &{filename?:string,inatObsQuery?:string}} InatObsPhotosCommandLineOptions
|
19
|
+
*/
|
20
|
+
|
12
21
|
// While I'm guessing the products of this data will be non-commercial, it's
|
13
22
|
// not clear how they'll be licensed so the ShareAlike clause is out, and
|
14
23
|
// they'll probably be derivative works so the "No Derivatives" clause should
|
@@ -18,7 +27,7 @@ const ALLOWED_LICENSE_CODES = ["cc0", "cc-by", "cc-by-nc"];
|
|
18
27
|
const DEFAULT_FILENAME = "inatobsphotos.csv";
|
19
28
|
|
20
29
|
/**
|
21
|
-
* @param {Taxon} taxon
|
30
|
+
* @param {import("../lib/taxon.js").Taxon} taxon
|
22
31
|
* @param {InatObsPhotosCommandLineOptions} options
|
23
32
|
* @return {Promise<InatApiObservation[]>}
|
24
33
|
*/
|
@@ -10,24 +10,26 @@ import { Program } from "../lib/program.js";
|
|
10
10
|
import { Taxa } from "../lib/taxa.js";
|
11
11
|
import { chunk, sleep } from "../lib/util.js";
|
12
12
|
|
13
|
+
/**
|
14
|
+
* @typedef {import("../lib/program.js").CommandLineOptions & {filename?:string}} InatTaxonPhotosCommandLineOptions
|
15
|
+
*/
|
16
|
+
|
13
17
|
// While I'm guessing the products of this data will be non-commercial, it's
|
14
18
|
// not clear how they'll be licensed so the ShareAlike clause is out, and
|
15
19
|
// they'll probably be derivative works so the "No Derivatives" clause should
|
16
20
|
// be respected.
|
17
|
-
const ALLOWED_LICENSE_CODES = [
|
18
|
-
"cc0", "cc-by", "cc-by-nc"
|
19
|
-
];
|
21
|
+
const ALLOWED_LICENSE_CODES = ["cc0", "cc-by", "cc-by-nc"];
|
20
22
|
|
21
23
|
const DEFAULT_FILENAME = "inattaxonphotos.csv";
|
22
24
|
|
23
25
|
/**
|
24
|
-
* @param {Taxon[]} taxa
|
25
|
-
* @return {Promise<InatApiTaxon[]>}
|
26
|
+
* @param {import("../lib/taxon.js").Taxon[]} taxa
|
27
|
+
* @return {Promise<import("../lib/utils/inat-tools.js").InatApiTaxon[]>}
|
26
28
|
*/
|
27
|
-
async function fetchInatTaxa(
|
28
|
-
const inatTaxonIDs = taxa.map(
|
29
|
-
const url = `https://api.inaturalist.org/v2/taxa/${inatTaxonIDs.join(
|
30
|
-
const resp = await fetch(
|
29
|
+
async function fetchInatTaxa(taxa) {
|
30
|
+
const inatTaxonIDs = taxa.map((taxon) => taxon.getINatID()).filter(Boolean);
|
31
|
+
const url = `https://api.inaturalist.org/v2/taxa/${inatTaxonIDs.join(",")}?fields=(taxon_photos:(photo:(medium_url:!t,attribution:!t,license_code:!t)))`;
|
32
|
+
const resp = await fetch(url);
|
31
33
|
if (!resp.ok) {
|
32
34
|
const error = await resp.text();
|
33
35
|
throw new Error(`Failed to fetch taxa from iNat: ${error}`);
|
@@ -39,74 +41,74 @@ async function fetchInatTaxa( taxa ) {
|
|
39
41
|
/**
|
40
42
|
* @param {InatTaxonPhotosCommandLineOptions} options
|
41
43
|
*/
|
42
|
-
async function getTaxonPhotos(
|
44
|
+
async function getTaxonPhotos(options) {
|
43
45
|
const errorLog = new ErrorLog(options.outputdir + "/errors.tsv");
|
44
46
|
const taxa = new Taxa(
|
45
47
|
Program.getIncludeList(options.datadir),
|
46
48
|
errorLog,
|
47
|
-
false
|
49
|
+
false,
|
48
50
|
);
|
49
|
-
const targetTaxa = taxa.getTaxonList(
|
51
|
+
const targetTaxa = taxa.getTaxonList();
|
50
52
|
|
51
53
|
const filename = path.join("data", options.filename || DEFAULT_FILENAME);
|
52
|
-
const writableStream = fs.createWriteStream(
|
53
|
-
const columns = [
|
54
|
-
|
55
|
-
"id",
|
56
|
-
"ext",
|
57
|
-
"licenseCode",
|
58
|
-
"attrName",
|
59
|
-
];
|
60
|
-
const stringifier = stringify( { header: true, columns: columns } );
|
54
|
+
const writableStream = fs.createWriteStream(filename);
|
55
|
+
const columns = ["name", "id", "ext", "licenseCode", "attrName"];
|
56
|
+
const stringifier = stringify({ header: true, columns: columns });
|
61
57
|
stringifier.pipe(writableStream);
|
62
58
|
const prog = new cliProgress.SingleBar({
|
63
59
|
format: "Downloading [{bar}] {percentage}% | ETA: {eta}s | {value}/{total}",
|
64
|
-
etaBuffer: targetTaxa.length
|
60
|
+
etaBuffer: targetTaxa.length,
|
65
61
|
});
|
66
|
-
prog.setMaxListeners(
|
67
|
-
prog.start(
|
62
|
+
prog.setMaxListeners(100);
|
63
|
+
prog.start(targetTaxa.length, 0);
|
68
64
|
|
69
65
|
// Fetch endpoint can load multiple taxa, but it will created some long URLs so best to keep this smallish
|
70
|
-
for (
|
71
|
-
const inatTaxa = await fetchInatTaxa(
|
72
|
-
for (
|
73
|
-
prog.increment(
|
74
|
-
const iNatTaxon = inatTaxa.find(
|
75
|
-
|
76
|
-
|
66
|
+
for (const batch of chunk(targetTaxa, 30)) {
|
67
|
+
const inatTaxa = await fetchInatTaxa(batch);
|
68
|
+
for (const taxon of batch) {
|
69
|
+
prog.increment();
|
70
|
+
const iNatTaxon = inatTaxa.find(
|
71
|
+
(it) => it.id === Number(taxon.getINatID()),
|
72
|
+
);
|
73
|
+
if (!iNatTaxon) continue;
|
74
|
+
// Just get the CC-licensed ones, 5 per taxon should be fine (max is 20 on iNat). Whether or not
|
77
75
|
const taxonPhotos = iNatTaxon.taxon_photos
|
78
|
-
.filter(
|
79
|
-
|
76
|
+
.filter((tp) =>
|
77
|
+
ALLOWED_LICENSE_CODES.includes(tp.photo.license_code),
|
78
|
+
)
|
79
|
+
.slice(0, 5);
|
80
80
|
|
81
|
-
for (
|
81
|
+
for (const taxonPhoto of taxonPhotos) {
|
82
82
|
const row = [
|
83
83
|
taxon.getName(),
|
84
84
|
taxonPhoto.photo.id,
|
85
|
-
String(
|
85
|
+
String(taxonPhoto.photo.medium_url).split(".").at(-1),
|
86
86
|
// Need the license code to do attribution properly
|
87
87
|
taxonPhoto.photo.license_code,
|
88
88
|
// Photographers retain copyright for most CC licenses,
|
89
89
|
// except CC0, so attribution is a bit different
|
90
|
-
(
|
91
|
-
taxonPhoto.photo.attribution.match(
|
92
|
-
|
93
|
-
|
90
|
+
taxonPhoto.photo.attribution.match(/\(c\) (.*?),/)?.[1] ||
|
91
|
+
taxonPhoto.photo.attribution.match(
|
92
|
+
/uploaded by (.*)/,
|
93
|
+
)?.[1],
|
94
94
|
];
|
95
|
-
stringifier.write(
|
95
|
+
stringifier.write(row);
|
96
96
|
}
|
97
97
|
}
|
98
98
|
// iNat will throttle you if you make more than 1 request a second.
|
99
99
|
// See https://www.inaturalist.org/pages/api+recommended+practices
|
100
|
-
await sleep(
|
100
|
+
await sleep(1_100);
|
101
101
|
}
|
102
102
|
prog.stop();
|
103
103
|
}
|
104
104
|
|
105
105
|
const program = Program.getProgram();
|
106
|
-
program
|
106
|
+
program
|
107
|
+
.action(getTaxonPhotos)
|
108
|
+
.description("Write a CSV to datadir with iNaturalist taxon photos")
|
107
109
|
.option(
|
108
110
|
"-fn, --filename <filename>",
|
109
|
-
"Name of file to write to the data dir"
|
111
|
+
"Name of file to write to the data dir",
|
110
112
|
);
|
111
113
|
|
112
114
|
await program.parseAsync();
|
package/types/classes.d.ts
DELETED
@@ -1,205 +0,0 @@
|
|
1
|
-
declare class CommandLineOptions {
|
2
|
-
datadir: string;
|
3
|
-
outputdir: string;
|
4
|
-
"show-flower-errors": boolean;
|
5
|
-
}
|
6
|
-
|
7
|
-
declare class Config {
|
8
|
-
getConfigValue(
|
9
|
-
prefix: string,
|
10
|
-
name: string,
|
11
|
-
subcategory?: string,
|
12
|
-
defaultValue?: string,
|
13
|
-
): string | undefined;
|
14
|
-
getCountyCodes(): string[];
|
15
|
-
getLabel(name: string, dflt: string): string;
|
16
|
-
}
|
17
|
-
|
18
|
-
declare class ErrorLog {
|
19
|
-
log(...args: any[]): void;
|
20
|
-
write(): void;
|
21
|
-
}
|
22
|
-
|
23
|
-
declare class Families {
|
24
|
-
getFamilies(): Family[];
|
25
|
-
getFamily(name: string): Family;
|
26
|
-
}
|
27
|
-
|
28
|
-
declare class Family {
|
29
|
-
getFileName(): string;
|
30
|
-
getName(): string;
|
31
|
-
getSectionName(): string;
|
32
|
-
getTaxa(): Taxon[];
|
33
|
-
}
|
34
|
-
|
35
|
-
declare class FlowerColor {
|
36
|
-
getColorCode(): string;
|
37
|
-
getColorName(upperCase?: boolean): string;
|
38
|
-
getFileName(): string;
|
39
|
-
getTaxa(): Taxon[];
|
40
|
-
}
|
41
|
-
|
42
|
-
declare class Genera {
|
43
|
-
getGenus(name: string): Genus;
|
44
|
-
}
|
45
|
-
|
46
|
-
declare class Genus {
|
47
|
-
getFamily(): Family;
|
48
|
-
getTaxa(): Taxon[];
|
49
|
-
}
|
50
|
-
|
51
|
-
declare class GlossaryEntry {
|
52
|
-
getMarkdown(): string;
|
53
|
-
getTermName(): string;
|
54
|
-
}
|
55
|
-
|
56
|
-
declare class InatObsOptions {
|
57
|
-
coords?: [number, number];
|
58
|
-
project_id?: string;
|
59
|
-
subview?: "grid" | "list" | "map";
|
60
|
-
taxon_id?: string;
|
61
|
-
}
|
62
|
-
|
63
|
-
declare class SiteGenerator {
|
64
|
-
copyIllustrations(flowerColors: FlowerColor[]): void;
|
65
|
-
mkdir(path: string): void;
|
66
|
-
writeTemplate(
|
67
|
-
content: string,
|
68
|
-
attributes: Record<string, string>,
|
69
|
-
filename: string,
|
70
|
-
): void;
|
71
|
-
}
|
72
|
-
|
73
|
-
declare class SynonymData {
|
74
|
-
Current: string;
|
75
|
-
Former: string;
|
76
|
-
Type: string;
|
77
|
-
}
|
78
|
-
|
79
|
-
declare class Taxa {
|
80
|
-
getFamilies(): Families;
|
81
|
-
getFlowerColors(): FlowerColor[];
|
82
|
-
getTaxon(name: string): Taxon;
|
83
|
-
getTaxonList(): Taxon[];
|
84
|
-
hasSynonym(name: string): boolean;
|
85
|
-
isSubset(): boolean;
|
86
|
-
}
|
87
|
-
|
88
|
-
type StatusCode = "N" | "NC" | "U" | "X";
|
89
|
-
declare class Taxon {
|
90
|
-
getBaseFileName(): string;
|
91
|
-
getBloomEnd(): number | undefined;
|
92
|
-
getBloomStart(): number | undefined;
|
93
|
-
getCalfloraID(): string;
|
94
|
-
getCalfloraName(): string;
|
95
|
-
getCalfloraTaxonLink(): string | undefined;
|
96
|
-
getCalscapeCommonName(): string | undefined;
|
97
|
-
getCalscapeName(): string;
|
98
|
-
getCommonNames(): string[];
|
99
|
-
getFamily(): Family;
|
100
|
-
getFESA(): string | undefined;
|
101
|
-
getFileName(): string;
|
102
|
-
getFlowerColors(): string[] | undefined;
|
103
|
-
getGenus(): Genus;
|
104
|
-
getGenusName(): string;
|
105
|
-
getGlobalRank(): string | undefined;
|
106
|
-
getHTMLLink(
|
107
|
-
href: boolean | string | undefined,
|
108
|
-
includeRPI?: boolean,
|
109
|
-
): string;
|
110
|
-
getINatID(): string;
|
111
|
-
getINatName(): string;
|
112
|
-
getINatSyn(): string | undefined;
|
113
|
-
getINatTaxonLink(): string;
|
114
|
-
getJepsonID(): string;
|
115
|
-
getLifeCycle(): string;
|
116
|
-
getName(): string;
|
117
|
-
getRPIID(): string | undefined;
|
118
|
-
getRPIRank(): string;
|
119
|
-
getRPIRankAndThreat(): string;
|
120
|
-
getRPIRankAndThreatTooltip(): string;
|
121
|
-
getRPITaxonLink(): string;
|
122
|
-
getStatus(): StatusCode;
|
123
|
-
getStatusDescription(config: Config): string;
|
124
|
-
getSynonyms(): string[];
|
125
|
-
isCANative(): boolean;
|
126
|
-
isNative(): boolean;
|
127
|
-
}
|
128
|
-
|
129
|
-
declare class TaxonData {
|
130
|
-
bloom_end: string;
|
131
|
-
bloom_start: string;
|
132
|
-
calrecnum: string;
|
133
|
-
calscape_cn?: string;
|
134
|
-
CESA: string;
|
135
|
-
"common name": string;
|
136
|
-
CRPR: string;
|
137
|
-
FESA: string;
|
138
|
-
flower_color: string;
|
139
|
-
GRank: string;
|
140
|
-
"inat id": string;
|
141
|
-
"jepson id": string;
|
142
|
-
life_cycle: string;
|
143
|
-
"RPI ID": string;
|
144
|
-
SRank: string;
|
145
|
-
status: StatusCode;
|
146
|
-
taxon_name: string;
|
147
|
-
}
|
148
|
-
|
149
|
-
type PhotoRights = "CC0" | "CC BY" | "CC BY-NC" | "C" | null;
|
150
|
-
|
151
|
-
type InatPhotoInfo = {
|
152
|
-
id: string;
|
153
|
-
ext: string;
|
154
|
-
licenseCode: string;
|
155
|
-
attrName: string | undefined;
|
156
|
-
};
|
157
|
-
|
158
|
-
type InatLicenseCode =
|
159
|
-
| "cc-by-nc-sa"
|
160
|
-
| "cc-by-nc"
|
161
|
-
| "cc-by-nc-nd"
|
162
|
-
| "cc-by"
|
163
|
-
| "cc-by-sa"
|
164
|
-
| "cc-by-nd"
|
165
|
-
| "pd"
|
166
|
-
| "gdfl"
|
167
|
-
| "cc0";
|
168
|
-
|
169
|
-
declare class InatCsvPhoto {
|
170
|
-
name: string;
|
171
|
-
id: number;
|
172
|
-
ext: string;
|
173
|
-
licenseCode: InatLicenseCode;
|
174
|
-
attrName: string;
|
175
|
-
}
|
176
|
-
|
177
|
-
declare class InatApiPhoto {
|
178
|
-
id: number;
|
179
|
-
attribution: string;
|
180
|
-
license_code: InatLicenseCode;
|
181
|
-
medium_url?: string;
|
182
|
-
url?: string;
|
183
|
-
}
|
184
|
-
|
185
|
-
declare class InatApiTaxon {
|
186
|
-
id: number;
|
187
|
-
taxon_photos: {
|
188
|
-
photo: InatApiPhoto;
|
189
|
-
}[];
|
190
|
-
}
|
191
|
-
|
192
|
-
declare class InatApiObservation {
|
193
|
-
observation_photos: {
|
194
|
-
photo: InatApiPhoto;
|
195
|
-
}[];
|
196
|
-
}
|
197
|
-
|
198
|
-
declare class InatObsPhotosCommandLineOptions extends CommandLineOptions {
|
199
|
-
filename?: string;
|
200
|
-
inatObsQuery?: string;
|
201
|
-
}
|
202
|
-
|
203
|
-
declare class InatTaxonPhotosCommandLineOptions extends CommandLineOptions {
|
204
|
-
filename?: string;
|
205
|
-
}
|