第十四天 - Mojo::DOM 实例
— 焉知非鱼随着最新版本的Mojolicious,Mojo::DOM获得了很多力量,我很兴奋尝试,但没有时间。最近,我在实际工作中遇到了一个问题(在工程,采购和施工,或简称EPC),我在很短的时间内用Mojo::DOM(和Mojolicious的其他部分)解决了 - 包括学习如何使用Mojo::DOM,我以前从未做过。
任务 - 简单,但乏味 #
3D模型和绘图是很棒的工具,但实际上并不是那么完美。建筑公差就是它们的原因,我们公司非常依赖激光扫描,我们在那里进入项目现场并创建一个竣工条件的点云。这会生成数百个需要花费数小时才能处理的文件 - 最近的一个项目有超过30亿个人点。这些对于我们所做的每个项目的工程,建模和构造都至关重要。
问题是当我们的3D建模软件(Tekla Structures)处理点云时,它会将每个文件名从人类可读的东西(例如Pipe Rack Area1)更改为散列,如2e9d52829f973c5b98f60935d8a9fa2b。当一个项目可能有几十个区域时,这不是非常用户友好,而您实际上只想一次加载一个或两个(平均面积为15gb!)。
点云提供的关键信息太难或太昂贵,无法直接建模
幸运的是,Tekla使用了许多任何人都可以编辑的标准文件格式 - 包括使用XML来描述它已处理的每个点云。当然,我可以手工编辑它们来更改名称,但是每次扫描和每个项目都必须这样做 - 这不是一个好的解决方案。
方便的是,XML文件包含哈希名称和原始文件名 - 所以我知道我可以使用Mojo::DOM来解析XML并将点云重命名为人类可读。这个简单的任务就是Mojolicious如何用相对较短的代码完成大量工作的完美例子。结果如下:
#!/usr/bin/perl
use Mojo::Base -base;
use Mojo::Util qw(getopt);
use Mojo::File;
use Mojo::DOM;
getopt 'p|path=s' => \my $path;
sub main {
# look in xml elements for laserscans that have hashes for names
my $file = Mojo::File->new($path, 'pointclouds.xml');
my $dom = Mojo::DOM->new($file->slurp);
# if 'Hash' is populated, rename_files(); otherwise ignore
for my $e ($dom->find('PointCloudData')->each) {
$e->{Folder} = rename_files($e) and $e->{Hash} = '' if $e->{Hash};
}
# save xml file so we don't try to rename the pointclouds again
$file->spurt($dom);
}
sub rename_files {
# rename pointcloud folder and database file
my $e = shift;
my $newname = $e->{Folder} =~ s/$e->{Hash}/$e->{Name}/r;
say "renaming: $e->{Folder} to:\n$newname";
rename $e->{Folder}, $newname || die $!;
rename $e->{Folder}.'.db', $newname.'.db' || die $!;
return ($newname);
}
main() if $path || die 'Please enter a path to the example files.';
不是 Web 应用程序 #
并非每次使用Mojolicious都必须是一个完整的应用程序 - 它的一部分可以像任何其他Perl模块一样使用。对于一个简单的脚本,它可以更快地达到您的实际目的。我现在一直使用以下行,即使我没有构建一个完整的Mojolicious应用程序。 Mojo::Base使我免于输入额外的锅炉板并启用严格,警告和我经常想要使用的其他好东西。
use Mojo::Base -base;
Mojo::Util - 只是为了好玩 #
我的实用程序的生产版本实际上加载了一个假的Mojolicious $app,以便我可以使用Mojolicious::Plugin::Config在myapp.conf中找到点云文件,但在这个版本中我使用Mojo::Util来获取点云文件路径,带有命令行选项–p。
getopt 'p|path=s' => \my $path;
要在示例文件上运行我的实用程序,必须先将pointclouds.xml中Folder的内容更改为保存它们的位置。
$ perl tekla_utility.pl --p 'path to example files'
Mojo::File - 永远不要再手动编写文件代码! #
Mojo::File使得读取pointclouds.xml所以我可以用Mojo::DOM简单解析它:
my $file = Mojo::File->new($path, 'pointclouds.xml');
my $dom = Mojo::DOM->new($file->slurp);
在过去的糟糕时期,每次我想读取文件时,我可能会手写15行(可怕的)代码。
Mojo::DOM - 它不能做什么? #
当然,Mojo::DOM可以轻松地在XML中找到正确的值 - 它还可以处理HTML和CSS选择器。基本上,我只是遍历PointCloudData的内容,它包含Tekla处理的每个点云的Folder,Hash和Name键:
for my $e ($dom->find('PointCloudData')->each) {
$e->{Folder} = rename_files($e) and $e->{Hash} = '' if $e->{Hash};
}
如果填充了Hash,我只运行rename_files,如果是,我将其清空,所以我不会再尝试重命名它们。 rename_files是关于我自己编写的唯一Perl代码 - 几乎所有其他内容都是从优秀的Mojolicious文档中直接复制和粘贴的!
替换在$newname中存储所需的文件和文件夹名称(非破坏性修饰符/ r允许我在不更改原始文件的情况下处理$e的副本)。然后我只需重命名点云文件夹和* .db文件(其中包含实际的点云数据)。
my $newname = $e->{Folder} =~ s/$e->{Hash}/$e->{Name}/r;
当我完成重命名点云时,我使用Mojo::File将$dom的内容保存回pointclouds.xml一行:
$file->spurt($dom)
好的一点是,当我在循环中更改$e - > {Folder}和$e -> {Hash}的内容时,将其保存回来只是起作用 - 我不需要过多考虑XML结构所有。有趣的是,保存$dom可以实现密钥,但Tekla似乎没有注意到。
注意点云的粒状性质 - 因为它们只是没有区域的点,越接近,它看起来就越粗糙
适用于所有技能水平 #
这只是我在日常工作中如何使用Mojolicious的一个例子。有时候,现有软件不能做你想做的事情,或者用一种无用的格式 - 可以用Mojolicious提供的许多工具解决的问题。你甚至不必是一个真正的程序员(我不是),或者根本不在软件行业工作。
我的下一个项目想法是重建我为Material Take Offs(MTO)编写的报告工具,以便与我们的工程师喜爱的Tekla一起使用我们的旧建模软件 - 我相信我会继续发现Mojolicious的良好用途。未来。