Wait the light to fall

第十四天 - Mojo::DOM 实例

焉知非鱼

img

随着最新版本的Mojolicious,Mojo::DOM获得了很多力量,我很兴奋尝试,但没有时间。最近,我在实际工作中遇到了一个问题(在工程,采购和施工,或简称EPC),我在很短的时间内用Mojo::DOM(和Mojolicious的其他部分)解决了 - 包括学习如何使用Mojo::DOM,我以前从未做过。

任务 - 简单,但乏味 #

3D模型和绘图是很棒的工具,但实际上并不是那么完美。建筑公差就是它们的原因,我们公司非常依赖激光扫描,我们在那里进入项目现场并创建一个竣工条件的点云。这会生成数百个需要花费数小时才能处理的文件 - 最近的一个项目有超过30亿个人点。这些对于我们所做的每个项目的工程,建模和构造都至关重要。

问题是当我们的3D建模软件(Tekla Structures)处理点云时,它会将每个文件名从人类可读的东西(例如Pipe Rack Area1)更改为散列,如2e9d52829f973c5b98f60935d8a9fa2b。当一个项目可能有几十个区域时,这不是非常用户友好,而您实际上只想一次加载一个或两个(平均面积为15gb!)。

img

点云提供的关键信息太难或太昂贵,无法直接建模

幸运的是,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似乎没有注意到。

img

注意点云的粒状性质 - 因为它们只是没有区域的点,越接近,它看起来就越粗糙

适用于所有技能水平 #

这只是我在日常工作中如何使用Mojolicious的一个例子。有时候,现有软件不能做你想做的事情,或者用一种无用的格式 - 可以用Mojolicious提供的许多工具解决的问题。你甚至不必是一个真正的程序员(我不是),或者根本不在软件行业工作。

我的下一个项目想法是重建我为Material Take Offs(MTO)编写的报告工具,以便与我们的工程师喜爱的Tekla一起使用我们的旧建模软件 - 我相信我会继续发现Mojolicious的良好用途。未来。