文章目录
- 一、iNaturalist 简介
- 二、R语言API:rinat
- 三、示例
- 3.1 获取观测数据
- 3.2 绘制可视化图像
- 函数用法
- 3.4 在区域网格中搜索
- 3.5 下载图片
- 3.51 提取图片 url
- 3.52 下载图片: R语言
- 3.53 下载图片: python
- 四、获取详细rinat包的文档
一、iNaturalist 简介
💻网址:https://www.inaturalist.org/
iNaturalist 是一个全球性的自然观察社区和生物多样性数据库,它允许用户记录和分享他们在自然界中的观察。这个平台由加州科学院(California Academy of Sciences)和国家地理学会(National Geographic Society)联合资助,并且得到了微软“AI for Earth”项目的支持,提供云计算资源和人工智能技术帮助提升图像识别能力。
用户可以在iNaturalist上记录他们遇到的各种生物,包括动物、植物、真菌等,并且可以上传相关的图片或描述。这些观察记录可以贡献给科学研究,帮助科学家和资源管理者了解生物多样性的分布和状况。iNaturalist社区鼓励用户参与讨论,鉴定物种,并与其他自然爱好者交流。
此外,iNaturalist还提供了一些额外的功能,比如创建项目(Projects)、运行生物多样性调查活动(Bioblitz)、以及通过图鉴(Life List)来管理和展示个人的自然观察记录。
iNaturalist不仅是一个数据记录平台,它还注重社区的建设和参与。它鼓励用户成为公民科学家,通过参与项目和活动来贡献自己的观察数据,这些数据可以被科学家用于研究和保护生物多样性。
总的来说,iNaturalist是一个强大的工具,它结合了社区的力量和科技的支持,旨在促进自然观察、生物多样性研究和环境保护。
🧩网站图片示例:
二、R语言API:rinat
R 语言提供了用于该网站的包。
rinat
是一个R语言的包,它提供了一个程序化接口来访问iNaturalist网站提供的API,以便下载由公民科学家提交的物种出现数据。这个包允许用户通过各种搜索参数来检索观察数据,例如物种名称、地点、日期、记录质量等。
以下是一些rinat
包的主要功能:
-
获取观察数据:使用
get_inat_obs()
函数,可以根据查询字符串、物种名称、地点、日期等条件来检索iNaturalist上的观察数据。 -
项目观察:通过
get_inat_obs_project()
函数,可以获取特定项目的所有观察数据,如果知道项目的ID或iNaturalist上的slug名称。 -
观察详情:使用
get_inat_obs_id()
函数,可以通过观察ID来检索特定观察的详细信息。 -
用户观察:
get_inat_obs_user()
函数允许你获取特定用户的所有观察数据。 -
物种统计:
get_inat_taxon_stats()
函数可以获取特定物种的统计信息。 -
用户统计:
get_inat_user_stats()
函数可以获取特定用户的统计信息。 -
制图功能:
inat_map()
函数可以快速创建基本地图,以可视化搜索结果。
安装rinat
包的步骤如下:
- 通过CRAN安装最新版本:
install.packages("rinat")
- 或者,从GitHub安装开发版本:
remotes::install_github("ropensci/rinat")
三、示例
3.1 获取观测数据
以get_inat_obs
函数为例: 参数
参数 | 描述 |
---|---|
query | 查询字符串,用于进行一般搜索。 |
taxon_name | 根据 iNaturalist 分类名称进行过滤。注意,这也会选择后代分类单元的观测。名称可能不是唯一的,匹配多个分类时可能返回空结果。 |
taxon_id | 根据 iNaturalist 分类 ID 进行过滤。与 taxon_name 类似,也会选择后代分类单元的观测。 |
place_id | 根据 iNaturalist 地点 ID 进行过滤。 |
quality | 数据的质量等级,必须是 “casual”(随意)或 “research”(研究)。如果留空,则返回所有质量的数据。 |
geo | 仅返回有地理参考的结果。如果设置为 TRUE ,将排除没有地理参考的数据。 |
annotation | 根据注解进行过滤。是一个长度为 2 的向量,第一个元素是术语 ID(例如 “1” 表示生命阶段),第二个元素是值 ID(例如 “2” 表示成体)。 |
year | 只返回指定年份的观测结果(只能是一个具体年份,不能是年份范围)。 |
month | 根据月份过滤观测结果,必须为 1 到 12 的数字。 |
day | 根据日期过滤观测结果,必须为 1 到 31 的数字。 |
bounds | 搜索范围的边界框,以经度(-180 到 180)和纬度(-90 到 90)表示。格式为 [南纬, 西经, 北纬, 东经] ,也可以提供 sf 或 sp 对象来确定边界框。 |
maxresults | 返回结果的最大数量,不应超过 10000。 |
meta | 布尔值。如果设置为 TRUE ,函数输出将是一个包含元数据和数据框的列表;如果为 FALSE (默认),则只返回数据框。 |
返回值
- 返回一个包含请求的观测数据的数据框。
- 如果
meta = TRUE
,则返回一个包含元数据和数据框的列表。
这个函数的过滤功能有时可能不稳定,尤其是 query
参数。在某些情况下,使用 taxon
字段进行科学名称过滤可能效果更好。
🟢搜索伦敦,2024年的图片:
library(rinat)
library(sf)
# 这是伦敦的行政区划数据文件,我前一篇文章写了
London <- st_read('E:/Analysis/zenodo/data/admin/boundaries/London.shp')
observations <- get_inat_obs(bounds = London ,quality ='research',maxresults = 10,year = 2024)
返回结果包含很多信息,打印返回数据的类型和列名:
> class(observations)
[1] "data.frame"
> colnames(observations)
[1] "scientific_name" "datetime" "description"
[4] "place_guess" "latitude" "longitude"
[7] "tag_list" "common_name" "url"
[10] "image_url" "user_login" "id"
[13] "species_guess" "iconic_taxon_name" "taxon_id"
[16] "num_identification_agreements" "num_identification_disagreements" "observed_on_string"
[19] "observed_on" "time_observed_at" "time_zone"
[22] "positional_accuracy" "public_positional_accuracy" "geoprivacy"
[25] "taxon_geoprivacy" "coordinates_obscured" "positioning_method"
[28] "positioning_device" "user_id" "user_name"
[31] "created_at" "updated_at" "quality_grade"
[34] "license" "sound_url" "oauth_application_id"
[37] "captive_cultivated"
使用image_url
就可以下载该图片。
比如这张:https://static.inaturalist.org/photos/444220864/medium.jpeg
3.2 绘制可视化图像
🟢(1) 简单的plot
绘制,根据经纬度绘制点即可:
# 绘制观测点,设置颜色
plot(observations$longitude, observations$latitude,
col = "blue", # 设置点的颜色为蓝色
pch = 19, # 设置点的形状,19 是实心圆点
xlab = "Longitude",
ylab = "Latitude",
main = "iNaturalist Observations in London 2024") # 设置图形标题
🟢(2)inat_map
函数绘制
map <- inat_map(observations,map = 'world', subregion = "UK")
这个貌似只能精确到国家,比如我搜索的是伦敦的数据,但绘图是英国的。
inat_map()
函数用于绘制 iNaturalist 观测数据的地图。该函数不仅可以自动绘制地图,还可以返回一个 ggplot
对象,供用户进一步修改和添加图层。
函数用法
inat_map(data, map = "usa", subregion = ".", plot = TRUE)
参数 | 描述 |
---|---|
data | 包含 iNaturalist 观测数据的数据框,通常通过 get_inat_obs() 获取,数据中必须包含经纬度。 |
map | 要绘制的地图区域,默认值为 "usa" (美国),可以参考 map 包的文档获取其他区域的名称。 |
subregion | 地图子区域名称,默认是 "." ,即整个指定区域,也可以根据需求绘制更小的子区域。 |
plot | 逻辑值,默认值为 TRUE ,表示直接绘制地图。如果为 FALSE ,则返回一个 ggplot 对象,用户可以进一步修改。 |
🟢(3)使用ggplot2
library(ggplot2)
# 将观测数据转换为 sf 对象,便于与伦敦边界一起绘制
# 使用 longitude 和 latitude 列作为坐标
observations_sf <- st_as_sf(observations, coords = c("longitude", "latitude"), crs = st_crs(London))
# 使用 ggplot2 绘制伦敦边界和观测数据
ggplot() +
geom_sf(data = London, fill = "lightgray", color = "black") + # 绘制伦敦边界
geom_sf(data = observations_sf, aes(color = species_guess), size = 3) + # 绘制观测数据,使用 species_guess 作为颜色
scale_color_viridis_d() + # 使用 Viridis 颜色刻度
labs(title = "iNaturalist Observations in London 2024", color = "Species") + # 添加标题和颜色标签
theme_minimal()
3.4 在区域网格中搜索
将目标区域分为多个网格,在每个网格中进行搜索,这样可以是的搜索结果更加均匀。
注意:网格尺度太大,或者搜索限制太宽松,会导致get_inat_obs函数返回太多值,会报错。
或者错误的边界会返回0个结果(网格区域表示为[南纬, 西经, 北纬, 东经],顺序不能错了
)。
🔹🔹示例代码:
# 加载所需的包
library(rinat)
library(dplyr)
library(sf)
library(ggplot2)
gc()
# 读取伦敦边界 shapefile
London <- st_read('E:/Analysis/zenodo/data/admin/boundaries/London.shp')
grid <- London %>%
# 将伦敦边界转换为英国国家网格坐标系(OSGB 1936,EPSG:27700),这是一个常用的投影系统,适合用于测量距离(以米为单位)。
st_transform(27700) %>%
st_make_grid(cellsize = 10000) %>%
st_sf() %>%
mutate(cell = seq(1:nrow(.))) %>%
select(cell, everything()) %>%
st_transform(4326)
# 统计每个网格的观测数据
observations_list <- lapply(1:nrow(grid), function(i) {
g <- grid[i, ] # 选择第i个网格
bbox <- st_bbox(g) # 获取边界框
bounds <- c(bbox["ymin"], bbox["xmin"], bbox["ymax"], bbox["xmax"]) # [南纬, 西经, 北纬, 东经]
print(paste("searching for observations in grid cell",i,"out of",nrow(grid)))
# 获取每个网格区域内的 iNaturalist 观测数据,限制为10个
obs <- get_inat_obs(bounds = bounds, maxresults = 10,quality = 'research',year = 2024)
print(paste("..success..adding",nrow(obs),"records"))
# 检查是否有观测数据返回
if (nrow(obs) > 0) {
return(obs)
} else {
return(NULL) # 没有数据返回NULL
}
})
# 将观测数据列表转换为数据框,去除NULL值
observations_df <- do.call(rbind, observations_list)
# 将观测数据转换为 sf 对象
observations_sf <- st_as_sf(observations_df, coords = c("longitude", "latitude"), crs = st_crs(London))
# 过滤观测数据,只保留在伦敦边界内的观测
observations_within_london <- observations_sf[st_intersects(observations_sf, London) %>% lengths > 0,]
# 如果有观测数据,转换为 sf 对象
if (nrow(observations_within_london) > 0) {
# 使用 ggplot2 绘制伦敦边界和观测数据
ggplot() +
geom_sf(data = London, fill = "lightgray", color = "black") + # 绘制伦敦边界
geom_sf(data = observations_within_london, aes(color = species_guess), size = 3, show.legend = FALSE) + # 绘制观测数据
scale_color_viridis_d() + # 使用 Viridis 颜色刻度
labs(title = "iNaturalist Observations in London 2024", color = "Species") + # 添加标题
theme_minimal()
} else {
print("没有找到任何观测数据。")
}
3.5 下载图片
其实就是根据图片的url下载,很多语言都可以实现的。
3.51 提取图片 url
你也可以根据需要,保存更多的变量,比如经纬度之类的。
# 保存图片的url
inat_urls <- observations_within_london %>%
st_drop_geometry() %>% # 去掉 geometry 列
select(id,image_url)%>%
mutate(id = paste0("i",id)) %>%
filter(!is.na(id) & !is.na(image_url))
library(tidyverse)
write_csv(inat_urls, paste0("./inat_urls_1.csv"))
3.52 下载图片: R语言
# 加载必要的包
library(tidyverse)
# 读取 CSV 文件
inat_urls <- read_csv("./inat_urls_1.csv")
# 确保 URL 列和 ID 列的名称正确
url_column <- "image_url"
id_column <- "id"
# 创建一个下载目录(如果没有的话)
dir.create("images", showWarnings = FALSE)
# 下载每个 URL 对应的图片
for (i in 1:nrow(inat_urls)) {
# 获取当前 URL 和对应的 ID
image_url <- inat_urls[[url_column]][i]
image_id <- inat_urls[[id_column]][i]
# 生成保存图片的文件名,使用 ID
image_name <- paste0("images/", image_id, ".jpg") # 假设文件格式为 .jpg,调整为实际格式
# 下载图片
tryCatch({
download.file(image_url, destfile = image_name, mode = "wb") # mode = "wb" 适用于二进制文件
message(paste("Downloaded:", image_name))
}, error = function(e) {
message(paste("Failed to download:", image_url))
})
}
3.53 下载图片: python
这是论文:Social media and deep learning reveal specific cultural preferences for biodiversity 的参考代码
下载中断后可以继续上次的下载,做法是:下载过的图片,在csv文件中会新增dl
列,值设置为Y
。
import time
import pandas as pd
import urllib.request
import urllib.error
import http
from io import BytesIO
from PIL import Image, ImageFile
ImageFile.LOAD_TRUNCATED_IMAGES = True
# csv 文件序号后缀
s = 1
# Set directory
proj_dir = "E:/R_Language"
# General functions
def open_image(entry):
"""function to open image"""
img = [] # empty img list object as default
image_url = entry.iloc[1] # start with lowest resolution image
if image_url != image_url:
print("...none available")
image_url = []
print(f"image found at {image_url}")
if len(image_url) != 0:
e = None # empty error object
n = 0 # create request error counter
while True:
try:
response = urllib.request.urlopen(image_url)
img = Image.open(BytesIO(response.read()))
img = img.convert('RGB')
img = [img.resize((400, 400))]
break
except (urllib.error.ContentTooShortError, ConnectionResetError) as ex:
print(ex)
print('error...retrying...')
n += 1
if n > 10: # after ten error messages, move on
break
time.sleep(1)
continue
except (urllib.error.HTTPError, http.client.IncompleteRead,
urllib.error.URLError) as ex:
print(ex)
if str(ex) in ("HTTP Error 403: Forbidden", "HTTP Error 404: Not Found", "HTTP Error 410: Gone"):
print('...does not exist...moving on...')
e = ex
break
else:
print('...retrying...')
time.sleep(2)
continue
return (img)
def next_id(df):
"""function to load index of last downloaded image"""
if 'dl' in df:
df = df.iloc[:, 2]
i = pd.Series.last_valid_index(df) + 1
else:
i = 0
return (i)
def main():
# image_urls = pd.read_csv(f"{proj_dir}/data/inat/urls/inat_urls_{s}.csv")
image_urls = pd.read_csv(f"{proj_dir}/inat_urls_{s}.csv")
# image_dir = f"{proj_dir}/data/inat/imgs" # results file id (later split between atts and scenes)
image_dir = f"{proj_dir}/images"
start_i = next_id(image_urls)
for i in range(start_i, len(image_urls)):
print(f"Analysing image {i + 1} out of {len(image_urls)}")
image = open_image(image_urls.iloc[i])
if image: # if list is not empty
print(f"...image exists, downloading...")
image[0].save(f"{image_dir}/{image_urls.iloc[i, 0]}.jpg")
image_urls.loc[i, 'dl'] = 'Y'
# image_urls.to_csv(f"{proj_dir}/data/inat/urls/inat_urls_{s}.csv", sep=',', index=False)
image_urls.to_csv(f"{proj_dir}/inat_urls_{s}.csv", sep=',', index=False)
else:
print(f"Image for metadata record {i + 1} does not exist, moving on...")
image_urls.loc[i, 'dl'] = 'N'
# image_urls.to_csv(f"{proj_dir}/data/inat/urls/inat_urls_{s}.csv", sep=',', index=False)
image_urls.to_csv(f"{proj_dir}/inat_urls_{s}.csv", sep=',', index=False)
if __name__ == '__main__':
main()
四、获取详细rinat包的文档
在R的控制台输入相关函数的帮助,即可查看全部参数和详细说明:
如:
help(get_inat_obs)