数图2.10.0版新增检索表工具。该工具本质上是个离线词典,具备分类检索表折叠交互功能。
一、数图检索表的特点
小编曾在2008年前后制作过一款名为《中国高等植物物种检索词典》的MDict词库。为实现检索表分支条目的折叠和跳转功能,该词库将检索表重构成很多词条,其折叠效果只是一种模拟,实质是词条间跳转。而数图检索表词典直接支持词条内检索表的折叠交互,使用体验有了提升。
数图检索表工具在检索表的展示上还有个小创新,即在折叠时,展示从一级分支到末端分支各分支的兄弟分支。这使得在看到当前分支判断路径的同时,还可以看到被否定的分支判断,且支持切换分支。这非常有利于提高工作效率。



二、使用方法
1. 获取词库
数图APP安装包不包含任何词库,用户需自行导入词库。小编制作了《中国植物志数图检索表词库》,词库数据与上文提到的MDict词库相同,即以中国植物志为主体,增加了拉丁学名解释等数据。图1中展示的内容即来自词库。该词库仅供学习使用,词库所用数据的版权归原作者所有。
上述词库可在百度网盘*载下**,文件名为“中国植物志_数图检索表词库_20230303_先解压看说明.zip”:
链接:https://pan.baidu.com/s/1p3AF88s5p4-nzzoQWsbRaQ?pwd=leve
提取码:leve
2. 导入词库
将*载下**得到的zip文件解压,将解压得到的dmkt文件发送给微信,然后在微信内点击该文件消息,选择“其它应用打开”,选择“数图”(或数图本、或数图田调等别名)。此时将启动数图,并开始导入。词库包含词条43000多条,导入需持续1分钟或更长,需耐心等待,直至提示导入成功。
3. 使用检索表
检索表工具的入口在首页“工具”卡片中的底部,“其它”栏内(见图2左)。检索表通过页面顶部的搜索框搜索词条。搜索模式为模糊搜索。
就《中国植物志数图检索表词库》而言,词条名为分类群的“中文名 + 拉丁名”,因而可使用中文名或拉丁名搜索。



三、制作词库
1. 词库文件格式
数图检索表词库为单纯的XML格式,并以“dmkt”为文件后缀名。文件的格式示例见下文。
<?xml version='1.0' encoding='utf-8'?>
<DataMapKeyTable>
<KeyTable uuid="e2deb147b98c11ed94b3f85971b57a51" title="木通科 Lardizabalaceae">
<FamilyChain>
<FamilyChainText>植物界 Planta</FamilyChainText>
<FamilyChainText>被子植物门 Angiospermae</FamilyChainText>
</FamilyChain>
<KeyTableDescriptions>
<KeyTableDescription tag="俗名">木通科</KeyTableDescription>
<KeyTableDescription tag="描述">木质匐本,很少为直立灌木(猫儿屎属Decaisnea Hook. f. et Thoms.)。茎缠绕或攀缘,木质部有宽大的髓射线;冬芽大,有2至多枚覆瓦状排列的外鳞片。叶互生,掌状或三出复叶,很少为羽状复叶(猫儿屎属),无托叶;叶柄和小柄两端膨大为节状。花辐射对称,单性,雌雄同株或异株,很少杂性,通常组成总状花序或伞房状的总状花序,少为圆锥花序,萼片花瓣状,6片,排成两轮,覆瓦状或外轮的镊合状排列,很少仅有3片;花瓣6,蜜腺状,远较萼片小,有时无花瓣;雄蕊6枚,花丝离生或多少合生成管,花药外向,2室,纵裂,药隔常突出于药室顶端而成角状或凸头状的附属体;退化心皮3枚;在雌花中有6枚退化雄蕊;心皮3,很少6-9,轮生在扁平花托上或心皮多数,螺旋状排列在膨大的花托上,上位,离生,柱头显著,近无花柱,胚珠多数或仅1枚,倒生或直生,纵行排列。果为肉质的骨葖果或浆果,不开裂或沿向轴的腹缝开裂;种子多数,或仅1枚,卵形或肾形,种皮脆壳质,有肉质、丰富的胚乳和小而直的胚。</KeyTableDescription>
<KeyTableDescription tag="描述">9属约50种,大部分产于亚洲东部,只有2属分布于南美的智利。我国有7属42种2亚种4变种,南北均产,但多数分布于长江以南各省区。</KeyTableDescription>
</KeyTableDescriptions>
<Branches>
<Branch>
<BranchDescription>茎直立;奇数羽状复叶有小叶13片以上;花杂性,无花瓣,组成总状花序再复合为圆锥花序;冬芽大,只有外鳞片2枚。</BranchDescription>
<BranchTarget>猫儿屎属 Decaisnea</BranchTarget>
</Branch>
<Branch>
<BranchDescription>茎攀缘;掌状复叶或三出复叶;花单性,有或无花瓣,组成腋生的总状花序;冬芽具多枚覆瓦状排列的外鳞片。</BranchDescription>
<Branches>
<Branch>
<BranchDescription>掌状复叶有小叶3一9片;小叶两侧对称;果较大,椭圆形、长圆形至圆柱形,长3厘米以上。</BranchDescription>
<Branches>
<Branch>
<BranchDescription>小叶边缘浅波状或全缘,顶凹人、圆或钝;肉质骨葖果沿腹缝线开裂;花丝分离,很短或近于无花丝,花药内弯。</BranchDescription>
<Branches>
<Branch>
<BranchDescription>萼片3,很少为4或5,卵圆形;雌花远比雄花大,形状不同。</BranchDescription>
<BranchTarget>木通属 Akebia</BranchTarget>
</Branch>
<Branch>
<BranchDescription>萼片6,披针形或线形;雌花仅略大于雄花,形状近似。</BranchDescription>
<BranchTarget>长萼木通属 Archakebia</BranchTarget>
</Branch>
</Branches>
</Branch>
<Branch>
<BranchDescription>小叶全缘,顶部通常渐尖或尾尖;萼片6;雄蕊分离或合生,具花丝,花药直;心皮3。</BranchDescription>
<Branches>
<Branch>
<BranchDescription>内、外两轮萼片形状通常近似且顶端钝;蜜腺状花瓣6枚,小;雄蕊分离。</BranchDescription>
<BranchTarget>八月瓜属 Holboellia</BranchTarget>
</Branch>
<Branch>
<BranchDescription>外轮萼片披针形,渐尖,内轮的通常线形,有6枚蜜腺状花瓣或无花瓣;雄蕊花丝合生为管状或上部分离。</BranchDescription>
<BranchTarget>野木瓜属 Stauntonia</BranchTarget>
</Branch>
</Branches>
</Branch>
</Branches>
</Branch>
<Branch>
<BranchDescription>三出复叶;侧小叶两侧不对称;花有6枚蜜腺状花瓣;果较小,卵形,长2厘米以下。</BranchDescription>
<Branches>
<Branch>
<BranchDescription>小叶纸质(开花时膜质);心皮3,每心皮具胚珠多数;浆果成串悬垂;种子多数。</BranchDescription>
<BranchTarget>串果藤属 Sinofranchetia</BranchTarget>
</Branch>
<Branch>
<BranchDescription>小叶革质;心皮多数,螺旋状排列,每心皮具1胚珠;浆果具柄,多个着生于一球形花托上;种子单生。</BranchDescription>
<BranchTarget>大血藤属 Sargentodoxa</BranchTarget>
</Branch>
</Branches>
</Branch>
</Branches>
</Branch>
</Branches>
</KeyTable>
</DataMapKeyTable>
需说明的是,每个“KeyTable”标签对应一个词条,其属性“uuid”用于识别词条,需保证唯一,否则导入后会被覆盖。KeyTable标签的“title”属性为检索表模糊搜索的目标。“Branches”标签部分为类群的检索表,数图检索表对其特殊处理,使得具备交互功能,但此部分并非必须。
2. 制作工具
小编使用Python制作词库。由于所用脚本还比较粗陋,本文暂且仅展示核心代码。如需要帮助请联系小编。
# 检索表
branches = []
branch_queue = []
key_table_root_sql = f"SELECT branch,leaf_taxon_id , branch_id FROM rtrees WHERE root_taxon_id = {id} AND father_branch_id ='root' ORDER BY sn"
cursor*ex.e**cute(key_table_root_sql)
key_table_root_rows = cursor.fetchall()
if len(key_table_root_rows) > 0:
for row in key_table_root_rows:
branch = {"description": row[0], "branches": []}
if row[1] is not None and row[1] != "":
target = get_entry_title(cursor, row[1])
if target:
branch["target"] = target
if row[2] is not None and row[2] != "":
branch["id"] = row[2]
branches.append(branch)
branch_queue = [item for item in branches]
while len(branch_queue) > 0:
father = branch_queue.pop(0)
father_id = father["id"]
if father_id is None:
continue
key_table_sql = f"SELECT branch,leaf_taxon_id, branch_id FROM rtrees WHERE root_taxon_id = {id} AND father_branch_id = '{father_id}' ORDER BY sn DESC"
cursor*ex.e**cute(key_table_sql)
key_table_rows = cursor.fetchall()
if len(key_table_rows) == 0:
continue
for row in key_table_rows:
son = {"description": row[0], "branches": []}
if row[1] is not None and row[1] != "":
target = get_entry_title(cursor, row[1])
if target:
son["target"] = target
if row[2] is not None and row[2] != "":
son["id"] = row[2]
branch_queue.insert(0, son)
father["branches"].insert(0, son)
entry = {"uuid": uuid,
"title": title,
"familyChain": family_chain,
"keyTableDescriptions": descriptions,
"branches": branches
}
entryEle = Element("KeyTable", uuid=uuid, title=title)
familyChainEle = Element("FamilyChain")
for node in family_chain:
nodeEle = Element("FamilyChainText")
nodeEle.text = node
familyChainEle.append(nodeEle)
entryEle.append(familyChainEle)
descriptionsEle = Element("KeyTableDescriptions")
entryEle.append(descriptionsEle)
for descr in descriptions:
descrEle = Element("KeyTableDescription", tag=descr['tag'])
descrEle.text = descr['description']
descriptionsEle.append(descrEle)
if len(branches) > 0:
branchesEle = Element("Branches")
entryEle.append(branchesEle)
branchStake = [b for b in branches]
fatherBranchStake = [branchesEle for i in range(0, len(branches))]
# 广度优先
while len(branchStake) > 0:
b = branchStake.pop(0)
fatherBranchEle = fatherBranchStake.pop(0)
branchEle = Element("Branch")
branchDescr = Element("BranchDescription")
branchDescr.text = b['description']
branchEle.append(branchDescr)
fatherBranchEle.append(branchEle)
if b.get('target') is not None:
branchTarget = Element("BranchTarget")
branchTarget.text = b['target']
branchEle.append(branchTarget)
if b.get('branches') is not None and len(b['branches']) > 0:
b["branches"].reverse()
branchesEle = Element("Branches")
branchEle.append(branchesEle)
for son in b["branches"]:
branchStake.insert(0, son)
fatherBranchStake.insert(0, branchesEle)
root.append(entryEle)
四、附记
图检索表以具有交互登录的检索表为特点,但并不要求词条必须由检索表部分,它就是一个特殊的词典工具。
目前数图检索表尚不支持图片、音频等媒体数据。
“数图”是一款自主研发的“通用田野调查数据采集软件”,是一款安卓应用。“数图”可以将传统调查表格转换为动态表单,有效提升作业效率。
“数图”集成十多个辅助工具,涉及地图测量、传感器测量、摄影测量、人工智能等多个领域,助力数据采集工作。目前已上架多个主流应用市场,您可按如下方式获取:
华为应用市场、百度手机助手、360手机助手、豌豆荚、PP助手,请搜索【数图】;
OPPO应用商店、VIVO应用商店,请搜索【数图本】;
小米应用商店,请搜索【数图田调】。