【R + Python】iNaturalist 网站图片下载 inat api

图片描述

文章目录

  • 一、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包的主要功能:

  1. 获取观察数据:使用get_inat_obs()函数,可以根据查询字符串、物种名称、地点、日期等条件来检索iNaturalist上的观察数据。

  2. 项目观察:通过get_inat_obs_project()函数,可以获取特定项目的所有观察数据,如果知道项目的ID或iNaturalist上的slug名称。

  3. 观察详情:使用get_inat_obs_id()函数,可以通过观察ID来检索特定观察的详细信息。

  4. 用户观察get_inat_obs_user()函数允许你获取特定用户的所有观察数据。

  5. 物种统计get_inat_taxon_stats()函数可以获取特定物种的统计信息。

  6. 用户统计get_inat_user_stats()函数可以获取特定用户的统计信息。

  7. 制图功能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)

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/900974.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

毕业设计选题:基于Python的招聘信息爬取和可视化平台

开发语言&#xff1a;Python框架&#xff1a;djangoPython版本&#xff1a;python3.7.7数据库&#xff1a;mysql 5.7数据库工具&#xff1a;Navicat11开发软件&#xff1a;PyCharm 系统展示 采集的数据列表 招聘数据大屏 摘要 本系统通过对网络爬虫的分析&#xff0c;研究智…

xlnt加载excel报错:xl/workbook.xml:2:2581: error: attribute ‘localSheetId‘ expected

解决方案 大家不一定能看懂&#xff0c;地址里说的啥意思&#xff0c;地址过去主要说明了从https://github.com/musshorn/xlnt/tree/issue_685合入可以解决问题&#xff0c;后面再想推送到官方地址&#xff0c;但没人维护了。 我这边直接给大家说一个结果就是&#xff1a;问题…

dbt-codegen: dbt自动生成模板代码

dbt项目采用工程化思维&#xff0c;数据模型分层实现&#xff0c;支持描述模型文档和测试&#xff0c;非常适合大型数据工程项目。但也需要用户编写大量yaml描述文件&#xff0c;这个过程非常容易出错且无聊。主要表现&#xff1a; 手工为dbt模型编写yaml文件&#xff0c;这过…

关于eclipse的workspace

如果项目很多&#xff0c;为了方便管理&#xff0c;最好不要是使用working set 对项目进行分组。一个workspace加载项目过多&#xff0c;即使进行分组&#xff0c;有些操作也很对所有项目生效。为了避免卡顿&#xff0c;建议直接使用workspace分组管理&#xff0c;而不是workin…

2024年妈杯MathorCup大数据竞赛A题超详细解题思路

2024年妈杯大数据竞赛初赛整体难度约为0.6个国赛。A题为台风中心路径相关问题&#xff0c;为评价预测问题&#xff1b;B题为库存和销量的预测优化问题。B题难度稍大于A题&#xff0c;可以根据自己队伍情况进行选择。26日早六点之前发布AB两题相关解题代码论文。 下面为大家带来…

Github优质项目推荐(第八期)

文章目录 Github优质项目推荐 - 第八期一、【manim】&#xff0c;66.5k stars - 创建数学动画的 Python 框架二、【siyuan】&#xff0c;19.5k stars - 个人知识管理软件三、 【GetQzonehistory】&#xff0c;1.3k stars - 获取QQ空间发布的历史说说四、【SecLists】&#xff0…

【C++篇】栈的层叠与队列的流动:在 STL 的韵律中探寻数据结构的优雅之舞

文章目录 C 栈与队列详解&#xff1a;基础与进阶应用前言第一章&#xff1a;栈的介绍与使用1.1 栈的介绍1.2 栈的使用1.2.1 最小栈1.2.2 示例与输出 1.3 栈的模拟实现 第二章&#xff1a;队列的介绍与使用2.1 队列的介绍2.2 队列的使用2.2.1 示例与输出 2.3 队列的模拟实现2.3.…

pair类型应用举例

在main.cpp里输入程序如下&#xff1a; #include <iostream> //使能cin(),cout(); #include <utility> //使能pair数据类型; #include <string> //使能string字符串; #include <stdlib.h> //使能exit(); //pair类型可以将两个相同的或不同类…

一个基于.NET8+WPF开源的简单的工作流系统

项目介绍 AIStudio.Wpf.AClient 是一个基于 WPF (Windows Presentation Foundation) 构建的客户端框架&#xff0c;专为开发企业级应用而设计。该项目目前版本为 6.0&#xff0c;进行了全面优化和升级&#xff0c;提供了丰富的功能和模块&#xff0c;以满足不同场景下的开发需…

张驰咨询:揭秘六西格玛项目如何“重塑”手术机器人集成度

项目背景 XR-1000型腔镜手术机器人是精智医疗公司最新推出的智能化手术设备&#xff0c;专注于微创外科手术&#xff0c;具有高度的精度和灵活性。随产品功能的扩展以及市场需求升级&#xff0c;系统集成度成为制约其性能提升的瓶颈。当前的设计中&#xff0c;机器人各模块的集…

C++20中头文件syncstream的使用

<syncstream>是C20中新增加的头文件&#xff0c;提供了对同步输出流的支持&#xff0c;即在多个线程中可安全地进行输出操作&#xff0c;此头文件是Input/Output库的一部分。包括&#xff1a; 1.std::basic_syncbuf&#xff1a;是std::basic_streambuf的包装器(wrapper)&…

Golang | Leetcode Golang题解之第509题斐波那契数

题目&#xff1a; 题解&#xff1a; type matrix [2][2]intfunc multiply(a, b matrix) (c matrix) {for i : 0; i < 2; i {for j : 0; j < 2; j {c[i][j] a[i][0]*b[0][j] a[i][1]*b[1][j]}}return }func pow(a matrix, n int) matrix {ret : matrix{{1, 0}, {0, 1}}…

格姗知识圈博客网站开源了!

格姗知识圈博客 一个基于 Spring Boot、Spring Security、Vue3、Element Plus 的前后端分离的博客网站&#xff01;本项目基本上是小格子一个人开发&#xff0c;由于工作和个人能力原因&#xff0c;部分技术都是边学习边开发&#xff0c;特别是前端&#xff08;工作中是后端开…

中电信翼康工程师:我在 Apache SeaTunnel 社区的贡献之旅

贡献者Github ID&#xff1a;luckyLJY 文章整理&#xff1a;曾辉 Apache SeaTunnel 作为一款强大的数据同步和转换工具&#xff0c;凭借其部署易用性、容错机制、数据源支持、性能优势、功能丰富性以及活跃的社区支持&#xff0c;成为了数据工程师们不可或缺的利器。 因其具有的…

LCD手机屏幕高精度贴合

LCD手机屏幕贴合&#xff0c;作为智能手机生产线上至关重要的一环&#xff0c;其质量直接关乎用户体验与产品竞争力。这一工艺不仅要求屏幕组件间的无缝对接&#xff0c;达到极致的视觉与触觉效果&#xff0c;还需确保在整个生产过程中&#xff0c;从材料准备到最终成品&#x…

Ansible 的脚本 --- playbooks剧本

playbooks 本身由以下各部分组成 &#xff08;1&#xff09;Tasks&#xff1a;任务&#xff0c;即通过 task 调用 ansible 的模板将多个操作组织在一个 playbook 中运行 &#xff08;2&#xff09;Vars&#xff1a;变量 &#xff08;3&#xff09;Templates&#xff1a;模板 &a…

【JavaEE初阶】网络编程TCP协议实现回显服务器以及如何处理多个客户端的响应

前言 &#x1f31f;&#x1f31f;本期讲解关于TCP/UDP协议的原理理解~~~ &#x1f308;感兴趣的小伙伴看一看小编主页&#xff1a;GGBondlctrl-CSDN博客 &#x1f525; 你的点赞就是小编不断更新的最大动力 &#x1f386;那么废话不多说…

提升数据处理效率:TDengine S3 的最佳实践与应用

在当今数据驱动的时代&#xff0c;如何高效地存储与处理海量数据成为了企业面临的一大挑战。为了解决这一问题&#xff0c;我们在 TDengine 3.2.2.0 首次发布了企业级功能 S3 存储。这一功能经历多个版本的迭代与完善后&#xff0c;逐渐发展成为一个全面和高效的解决方案。 S3…

STM32应用详解(9)使用USART远程控制LED

文章目录 前言一、使用USART远程控制LED二、代码实现及分析1.main函数2.UART串口初始化函数 前言 学习使用USART远程控制&#xff1a;在PC端通过串口助手输入数字&#xff0c;控制开发板上的LED的亮灭。 一、使用USART远程控制LED 《usart.c》文件完成UART串口初始化&#xf…

Radio单选框组件封装 (学习笔记)

文章目录 前言一、单选框基础布局二、使用这个组件总结 前言 单选框组件的封装 一、单选框基础布局 首先要现在文件radioBtn.vue文件 <script setup lang"ts"> defineProps<{options: {label: stringvalue: string | number}[]modelValue?: string | n…