https://www.ibm.com/developerworks/cn/opensource/os-autocad/

对开源库使用 AutoCAD 文件格式

读取 DWG 和 DXF 文件格式

Christopher Michaelis
2011 年 12 月 02 日发布

简介

很多开发人员和地理信息系统
(GIS) 专家一直无法使用 Drawing Interchange Format (DXF) 或 "drawing" (DWG)
格式的文件。这些 AutoCAD 格式的文件通常需要安装了 Windows®
和 AutoCAD 才能打开。借助几个便捷的开源库,您的应用程序就可以读取任何操作系统上的 DXF 和
DWG 文件,并且不产生任何成本。在本文中,您将构建一个转换器来将这些文件格式转换成更为开放的 ESRI
shapefile 或 keyhole markup language (KML)
格式。商业的和开源软件大都使用 ESRI Shapefile 格式,而 Google Earth 和 Google Maps 则主要使用
KML格式。

AutoCAD DWG 和 LibreDWG

最为常见的 AutoCAD 格式是 “drawing” 格式,文件扩展名的结尾为 .dwg。只有少数几款软件应用程序可以读取此格式,这个格式是在 AutoCAD 中保存文件时的默认格式。不过,您可以使用开源库 LibreDWG(参阅 参考资料)来读取这些文件。该文件格式包含一个控件块(内含代表此文件内的形状的额外块),以及面向 model spacepaper space
的块(它们代表的存在于此文档内的坐标偏移量)。

若要使用这个库,可以打开此文档并读取文件,然后依次循环主控制块内的每个块,如 清单 1 所示。

清单 1. 打开一个 DWG 文件并依次循环主控制块
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Dwg_Data dwg = new Dwg_Data();
int errno = dwg_read_file((char *)inputFilename, dwg);
if (errno) {
  fprintf(stderr, "Could not open DWG. Returned error code: $d\n", errno);
  delete dwg;
}
 
Dwg_Object_BLOCK_CONTROL * ctrl = dwg->object[0].tio.object->tio.BLOCK_CONTROL;
dumpBlock(ctrl->model_space);
dumpBlock(ctrl->paper_space);   
 
for (int i = 0; i < ctrl->num_entries; i++) {
  dumpBlock(ctrl->block_headers[i]);
}  
dwg_free(dwg);

每个块可代表任一几何形状:线、圆、弧、锚定在某个位置的文本或插入(要被应用到后面块的一个偏移量)。可以通过访问 get_first_owned_object
get_next_owned_object 所返回的块对象的属性来依次处理它们,如
清单 2 所示。

清单 2. 用 get_first_owned_object
和 get_next_owned_object 读取对象
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
void InputFormatDWG::dumpBlock(Dwg_Object_Ref * block) {
  if (!block) return;
  if (!block->obj) return;
  if (!block->obj->tio.object) return;
 
  Dwg_Object_BLOCK_HEADER * header = block->obj->tio.object->tio.BLOCK_HEADER;
  Dwg_Object * obj = get_first_owned_object(block->obj, header);
  while (obj) {
    if (obj->type == DWG_TYPE_LINE) {
      Dwg_Entity_LINE * line = obj->tio.entity->tio.LINE;
      printf("Line starting at (%f, %f, %f) ending at (%f, %f, %f)\n", line->start.x,
              line->start.y, 0, line->end.x, line->end.y, 0);
      // Don't delete "line" - dwg_free will do this
    }
 
    obj = get_next_owned_object(block->obj, obj, header);
  }
}

这样一来,用 LibreDWG 读取一个 DWG 文件就成为了一个有始有终的顺序流。当在
C++ 内实现 LibreDWG 时,务必要将
dwg.h 包括在
extern "C" 块内以避免日后遇到链接器错误。以下是一个例子:

1
2
3
extern "C" {
    #include <dwg.h>
}

这个库所需的前提条件是 autoconfswigtexinfo
python-dev 包以及编译器包(如果使用 Debian 或 Ubuntu 要用到 build-essential)。在命令行输入如下命令来通过下载构建这个库:

1
git clone git://git.sv.gnu.org/libredwg.git

. . . 然后再输入:

1
./autogen.sh && ./configure && make && sudo make install

AutoCAD DXF 和 dxflib

DXF 格式是 AutoCAD 中的一个导出选项。正因如此,相比 DWG,这个格式在更多应用程中受到支持,并且相关的规范也已发布(参阅 参考资料 获得完整 DXF 规范的链接)。可以使用开源 dxflib 库读取这些文件。与
LibreDWG 不同,通过自己的顺序编码来读取一个 DXF 文件的受驱动感要少。实际上,使用 dxflib 感觉上就如同是编写事件驱动代码。

通过调用 DL_Dxf 对象的 in 函数并将指针传递给一个继承 DL_CreationAdapter
抽象类来打开此文件。in 函数运行时,会调用传递给它的类中的几个函数。如今,有数十个这样的函数(参阅 参考资料 中的 DXFLib 程序员手册链接),但是在大多数情况下,受关注最多的有如下这几个函数:addPointaddLine
addCircle
addVertex。您只需实现您所关注的那些函数;剩下的您可以忽略。清单
3
显示了一个加载 DXF 文件并从此文件只读取线的简单例子。

清单 3. 加载 DXF 文件并只读取线
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
LineReader.h:
#ifndef LINEREADER_H
#define LINEREADER_H
 
#include "dxflib/src/dl_dxf.h"
#include "dxflib/src/dl_creationadapter.h"
#include <stdio.h>
 
class LineReader: public DL_CreationAdapter {
    public:
        // Our functions:
        void readLines(const char * filename);
 
        // Overloading from parent DL_CreationAdapter:
        void addLine(const DL_LineData& data);
 
 
};
#endif
 
LineReader.cpp:
void LineReader::readLines(const char * filename) {
  DL_Dxf * getData = new DL_Dxf();
  if (!getData->in(filename, this)) {
    fprintf(stderr, "Could not retrieve data from input file.\n");
    delete getData;
    exit(1);
  
  delete getData;
}
 
void LineReader::addLine(const DL_LineData& data) {
  printf("Line starting at (%f, %f, %f) ending at (%f, %f, %f)\n",
          data.x1, data.y1, data.z1, data.x2, data.y2, data.z2);
}

与 DWG 相似,DXF 格式也可以包含 insert,它代表的是要应用到插入后所遇到的几何特性中的偏移量。这些插入必须存储在内部且被应用到它们遇到的坐标。同样地,添加 polyline(具有多个顶点的线)需要存储一些数据。这个库先是调用
addPolyline,表明要创建的是线,然后再为这条线的每个顶点调用一次 addVertex。最后,当调用
endEntityendBlock 时,加线操作结束,这时就具有了一条完整的线,并可呈现它,将它导出至一个新格式或采取其他操作。

可以通过简单地在命令行输入如下命令来构建和安装 DXF 库:

1
./configure && make && sudo make install

您可能会收到有关 strcasecmp
以及 strlen 未声明的错误消息。dxflib 库在构建之初是为了用于 GCC/G++ 4.2 ,但是在 4.3 版本中使用会发生一些头文件重组。要修复这个错误,需要在其他包含附近的 src/dl_writer.h 和
src/dl_writer_ascii.h 内以及
#include <cstring>
#include <cstdlib> 内添加几个包含。

注意:在 下载 部分给出的转换器源代码内所包含的 dxflib 副本都已实施这些修改,所以只有从 dxflib 网站直接下载 dxflib 时,才需要应用这个修改。

KML 和纯文本/Xerces-C++

Google Earth 和 Google Maps 使用的 KML 格式是 XML 的一种专门形式;因此,可以使用诸如 Xerces-C++ XML Parser(参阅
参考资料 获得相关链接)这样的库来处理 KML 文件。
在读取这些格式时,使用像 Xerces-C++ 这样的正式库来处理可能遇到的更为复杂的结构是相当可取的。而在编写时,
通常只要使用简单的内置语言函数就可以来编写文本文件,并生成合适的文本。

一个基本的 KML 文件包含一个 Document
节,内含一个名称和描述。这个文件还可能包含一个或更多的文件夹(用来逻辑地组织各种形状),并且在每个文件夹内是一些 placemark。这些
placemark 是一些实际的形状,可定义为 LineString、Point、Polygon 或其他类型。编写一个 KML
文件需要编写代表各种形状的相应格式化文本,如 清单 4 所示。

清单 4. 一个简单的 KML 文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?xml version="1.0" encoding="UTF-8"?>
<kml xmlns="http://www.opengis.net/kml/2.2">
<Document>
  <name>test</name>
  <open>1</open>
  <description>Converted from AutoCAD DXF or DWG</description>
  <Folder>
    <name>test</name>
    <Placemark>
      <LineString>
        <tessellate>0</tessellate>
        <coordinates>27.54998,82.27393,0.00000 39.72346,9.25601,0.00000</coordinates>
      </LineString>
    </Placemark>
  </Folder>
</Document>
</kml>

您可能还会碰到 KMZ 文件,它们是用 ZIP 压缩了的 KML 文件。参阅 参考资料 获得有关 KML 和完整 KML 文档的初学者教程的链接。

ESRI Shapefile 和 GDAL/OGR

shapefile 格式是由 ESRI(参阅 参考资料
获得有关完整技术描述的链接)发布的一个商业(但是开放)二进制数据格式。您可以使用开源 OGR Simple
Feature Library 来轻松访问这些文件,OGR Simple Feature Library 是
Geospatial Data Abstraction
Layer(GDAL)的一部分。在这个示例转换器中,只需要输出,但是这个库还查以简化形状数据的读取。写数据则需要打开一个输出数据源并在其内创建一个数据层
。您可以创建字段来存储每个形状的非空间数据。创建了输出数据源之后,就可以将多个形状(称为 特性)添加到数据源。创建一个特性还需要创建一个几何形状,比如一个点或线,并将它与此特性相关联。此特性可充当所有字段/非空间数据和几何数据的容器。

清单 5 显示了创建一个新的 shapefile
数据源和数据集内的一个点所需要的代码。参阅 参考资料 中的 OGR
C++ 读/写教程和 OGR 类的分层结构和文档链接,来获得有关使用 OGR 的更多信息以及相关库的下载链接。大多数 Linux® 发布存储库都将 GDAL
库和头文件包含在 libgdal1-1.7.0
libgdal1-dev(各版本可能会有所不同)。

清单 5. 创建一个内含一个点的 shapefile
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
OGRRegisterAll();
OGRSFDriver drv = OGRSFDriverRegistrar::GetRegistrar()->GetDriverByName("ESRI Shapefile");
if (drv == NULL) {
  fprintf(stderr, "Could not open Shapefile OGR driver.\n");
  return;
 
OGRDataSource ds = drv->CreateDataSource(filename, NULL);
if (ds == NULL) {
  fprintf(stderr, "Could not create output file.\n");
  return;
}
 
OGRLayer lyr = ds->CreateLayer("Layer", NULL, wkbPoint, NULL);
if (lyr == NULL) {
  fprintf(stderr, "Could not create layer.\n");
  return;
}
 
// Add an ID field
OGRFieldDefn newField("id", OFTInteger);
newField.SetWidth(32);
lyr->CreateField(&newField);
 
if (!lyr) {
  fprintf(stderr, "No output layer is available.");
  return;
}
   
OGRFeature * newFeat = OGRFeature::CreateFeature(lyr->GetLayerDefn());
newFeat->SetField("id", lyr->GetFeatureCount(1));
OGRPoint point;
point.setX(15.653);
point.setY(43.783);
point.setZ(0);
newFeat->SetGeometry(&point);
lyr->CreateFeature(newFeat);
   
// Clean up your memory
OGRFeature::DestroyFeature(newFeat);
 
if (ds) {
  // Will trigger saving the file and also
  // clean up any layer references from Create/Get Layer calls
  OGRDataSource::DestroyDataSource(ds);
}

实现文件格式库

有三种主要的方式可在软件内实现一个文件格式库。第一种方式为应用程序内的本地实现,它要求您必须从代码中直接使用这些库,这就意味着必须要根据绑定的可用性以特定的一组语言来编写代码。它还需要牢固的链接,在更新库版本时,可能会出现小错误。不过,应用程序内的直接实现的确会让用户在使用此格式时感觉更为顺畅。

另一种方法是为支持所想要的文件格式的这个应用程序编写一个插件或扩展。这种方法提供了文件格式库和应用程序代码之间某种程度上的分离。如果应用程序已经具备了一种插件框架,那么这种方法将是一个很好的选择。比如,Quantum
GIS(一种常见的桌面 GIS 应用程序)就使用了一种插件架构;这样的插件就让您可以直接在应用程序内使用界定了的文本文件。

最后也是最为简单的一种方法是创建一个独立的转换器,它可以转换两个或多个文件格式。这种技术的优势是可以不管数据的最终目标而进行重用,缺点是为用户增加了一个步骤。

从 DXF/DWG
到 KML/Shapefile 的一个命令行转换器

在本例中,采用的是更为简单、可用性更好的创建文件格式转换器的方式。我们的目标是创建一种转换器,既能轻松进行扩展来处理额外格式又能尽可能地将任何特定的输入或输出格式的逻辑保持分离。为实现这些目标,需要定义两个抽象类:InputFormat
OutputFormat(参见 清单
6
)。

清单 6. InputFormat 和 OutputFormat 抽象类定义
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
OutputFormat.h:
#ifndef OUTPUTFORMAT_H
#define OUTPUTFORMAT_H
 
#include "OutputFormatTypes.h"
#include <vector>
 
class OutputFormat {
  public:
    OutputFormat() {};
    ~OutputFormat() {};
 
    // Standard feature types:
    virtual void addPoint(OutputFeaturePoint newPoint) = 0;
    virtual void addLine(OutputFeatureLine newLine) = 0;
    virtual void addPolyLine(std::vector<OutputFeaturePoint *> newPoints) = 0;
    virtual void addPolygon(std::vector<OutputFeaturePoint *> newPoints) = 0;
 
    // For approximating text on DXF/DWG with a separate point layer with
    // a label attribute:
    virtual void addText(OutputFeaturePoint location, const char * text) = 0;
 
    // The cleanup function
    virtual void finalizeOutput() = 0;
};
#endif
 
InputFormat.h:
#ifndef INPUTFORMAT_H
#define INPUTFORMAT_H
 
#include "OutputFormat.h"
 
class InputFormat {
  public:
    InputFormat() {};
    ~InputFormat() {};
    virtual void readFeaturesInto(OutputFormat * outputHandler) = 0;
};
#endif

所实现的任何格式都必须继承自这些类中的一个。这样一来,主函数以及程序的入口点就能专门确定要实例化哪个类。在定义好输入文件和输出文件后,通过如下的一行代码就可以执行转换:

1
input->readFeaturesInto(output);

主函数的作用(参见 清单 7)就继而变成了单纯地实例化恰当的输入和输出格式,并最后以 readFeaturesInto 函数调用告终。

清单 7. AutoCAD 转换器的 main() 函数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
void usage(char * me) {
  printf("Usage: %s inputfile outputfile [point|line|polygon]\n", me);
  printf("Input formats supported:\n");
  printf("\tAutoCAD DXF (*.dxf)\n");
  printf("\tAutoCAD DWG (*.dwg)\n");
  printf("Output formats supported:\n");
  printf("\tText (*.txt)\n");
  printf("\tESRI Shapefile (*.shp)\n");
  printf("\tKeyhold Markup Language (*.kml)\n");
  printf("\nInput format and output format are determined automatically by file extension.
          \n");
  printf("If you use a shapefile as the output format, please additionally specify\n");
  printf("point, line, or polygon output shapefile type.\n");
}
 
int main(int argc, char * argv[]) {
  if (argc < 3) {
    usage(argv[0]);
    return 1;
  }
 
  OutputFormat * output = NULL
  InputFormat * input = NULL;
  struct stat fileExists;
 
  // Set up output format first...
  std::string outFile = argv[2];
  if (outFile.rfind('.') == std::string::npos) {
    printf("I couldn't make sense of your output filename's extension: %s. Please use
            filename.shp, filename.kml, or filename.txt.\n", argv[2]);
    return 1;
  }
  if (outFile.substr(outFile.rfind('.')+1) == "txt") {
    printf("Setting up output file %s...\n", argv[2]);
    output = new OutputFormatText(argv[2]);
  }
  else if (outFile.substr(outFile.rfind('.')+1) == "shp") {
    if (argc < 4) {
      printf("When specifying shapefile output, please also specify 'point', 'line', or
              'polygon'. See usage.\n");
      return 1;
    }
    std::string textAttributeFile = outFile.substr(0, outFile.rfind('.')) + "_text.shp";
    OGRwkbGeometryType type;
    if (strcmp(argv[3], "line") == 0) {
      type = wkbLineString;
    }
    else if (strcmp(argv[3], "point") == 0) {
      type = wkbPoint;
    }
    else if (strcmp(argv[3], "polygon") == 0) {
      type = wkbPolygon;
    }
    else {
      printf("I didn't understand %s. Please use point, line, or polygon.\n", argv[3]);
      return 1;
    }
    printf("Setting up output file %s...\n", argv[2]);
    output = new OutputFormatSHP(argv[2], textAttributeFile.c_str(), outFile.substr(0,
                 outFile.rfind('.')).c_str(), type);
  }
  else if (outFile.substr(outFile.rfind('.')+1) == "kml") {
    printf("Setting up output file %s...\n", argv[2]);
    output = new OutputFormatKML(argv[2], outFile.substr(0, outFile.rfind('.')).c_str());
  }
 
  // Next grab the input file
  std::string inFile = argv[1];
  if (inFile.rfind('.') == std::string::npos) {
    printf("I couldn't make sense of your input filename's extension: %s. Please use
            filename.dxf or filename.dwg.\n", argv[1]);
    delete output;
    return 1;
  }
  if (stat(argv[1], &fileExists) != 0) {
    printf("The specified input file does not exist or is not accessible: %s\n", argv[1]);
    return 1;
  }
  if (inFile.substr(inFile.rfind('.')+1) == "dxf") {
    input = new InputFormatDXF(argv[1]);
    printf("Setting up input file %s...\n", argv[1]);
  }
  else if (inFile.substr(inFile.rfind('.')+1) == "dwg") {
    input = new InputFormatDWG(argv[1]);
    printf("Setting up input file %s...\n", argv[1]);
  }
 
  if (!input) {
    printf("The input file was not recognized or could not be opened.\n");
    return 1;
  }
  if (!output) {
    printf("The output file was not recognized or could not be opened.\n");
    return 1;
  }
 
  printf("Converting file...\n");
  input->readFeaturesInto(output);
  output->finalizeOutput();
  printf("Done!\n");
 
  delete input;
  delete output;
  return 0;
}

InputFormat
类内惟一必要的函数就是 readFeaturesInto,该函数向各个输入格式公开它是如何真的将特性(形状)提供给所配备的输出类。
然而,OutputFormat 类则具有多个更必要的函数,如用来添加各种几何形状的函数。您可以为点和线定义您自己的类,以便您可以提供双精度值而非整数值的参数以及 z-轴。

ESRI Shapefile 格式具有一个重要的限制;它只能存储单个类型的形状(比如,只存储线、点或多边形)。而本文中所讨论的其他格式 DXF、DWG 和 KML 均没有这个限制。在
OutputFormatSHP 类内,查看将要创建的 shapefile
类型;如果不是正确的类型,就要尽力处理好它。在创建一个点 shapefile
时,若系统提示添加一个多线形状,那么就以单个点添加每个顶点。在创建一个线 shapefile
时,如果系统提示添加一个点,一个警告就会写入到标准错误以表明此形状被忽略。DXF 和 DWG
文件格式还支持弧和圆,您可以在输出格式中通过创建多个小线段来近似模拟圆或弧。

在编译这个示例应用程序时,可能会收到与 dwg.h 相关的错误消息。dxflib 和 LibreDWG
库均定义了全局定义
THICKNESS,所以要在其安装的位置(比如 /usr/local/include/dwg.h)编辑这个 dwg.h 文件及更改 THICKNESS 常数的名称或在最后添加一个下划线(_)。(在本示例代码中并未这么做。)

注意: 参阅本文 下载 部分获得本示例文件格式转换器的全部源代码。

将这个转换器包裹到一个网站

此该,您已经拥有一个功能完善的命令行文件格式转换器。但是,大多数询问如何读取这些文件的用户通常都是不了解命令行,或不能自己从源代码构建一个转换器。所以,很有必要为这个工具提供一个简单的基于
Web
的界面,以便需要快速转换一两个文件的人们能够轻松地实现转换。同时,还可以创建一个系统,在这个系统中当文件被添加到磁盘上的一个特定的目录时可自动实现转换,这个对于
FTP 交互或通过一个 CIFS 文件共享访问文件非常有用。

首先,为各种 shapefile 输出格式类型创建一组目录:

  • uploads_KML 和 uploads_Text 分别用于 KML 和文本输出格式
  • uploads_Point、uploads_Polyline 和 uploads_Polygon 分别用于各种类型的 shapefile 输出格式

上载至上述任何一个位置的 DXF 或 DWG 文件会被转换成该目录名所指示的那种格式。输出也会被放置在另一个名为 outputs
的目录中。

有了这样的设置,就可以构造一个简单的 BASH shell 脚本(参见 清单 8)来检查此位置的 DWG 和 DXF 文件并且通过适当的命令行参数触发这个转换器。为了有助于之后的调试,这个脚本还能将所有已上载的输出和所生成的输出一起保存到一个具有时间戳的 .zip 文件以供管理使用。还可以将这个脚本设置成定期运行的 cron 作业或供 FTP 和 CIFS (Windows 文件共享)使用自动转换器。

清单 8. 用来查找待转换的文件并将这些文件传递至转换器的 BASH 脚本
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
#!/bin/bash -u
 
# Polyline Shapefile queue
cd /var/www/acadconverter.chrismichaelis.com/uploads_Polyline
for file in /var/www/acadconverter.chrismichaelis.com/uploads_Polyline/*.d*; do
  [[ "$file" =~ .dxf$ || "$file" =~ dwg$ ]] && {
    base=$(basename "$file")
    base=${base%.dwg}
    base=${base%.dxf}
    /var/www/acadconverter.chrismichaelis.com/bin/AutoCADConverter \
                "$file" "$base.shp" line
    [ -e "../outputs/$base.zip" ] && rm -f "../outputs/$base.zip"
    zip "../outputs/$base.zip" "$base.shx" "$base.shp" "$base.dbf" \
                "${base}_text.shp" "${base}_text.shx" "${base}_text.dbf"
    zip "../uploads_done/$(date +%s)_$base.zip" "$base.shx" "$base.shp" "$base.dbf" \
                "${base}_text.shp" "${base}_text.shx" "${base}_text.dbf" "$file"
    rm -f "$file" "$base.shx" "$base.shp" "$base.dbf" "${base}_text.shp" \
                "${base}_text.shx" "${base}_text.dbf"
  }
done
 
# Polygon Shapefile queue
cd /var/www/acadconverter.chrismichaelis.com/uploads_Polygon
for file in /var/www/acadconverter.chrismichaelis.com/uploads_Polygon/*.d*; do
  [[ "$file" =~ .dxf$ || "$file" =~ dwg$ ]] && {
    base=$(basename "$file")
    base=${base%.dwg}
    base=${base%.dxf}
    /var/www/acadconverter.chrismichaelis.com/bin/AutoCADConverter "$file" \
                "$base.shp" polygon
    [ -e "../outputs/$base.zip" ] && rm -f "../outputs/$base.zip"
    zip "../outputs/$base.zip" "$base.shx" "$base.shp" "$base.dbf" \
                "${base}_text.shp" "${base}_text.shx" "${base}_text.dbf"
    zip "../uploads_done/$(date +%s)_$base.zip" "$base.shx" "$base.shp" "$base.dbf" \
                "${base}_text.shp" "${base}_text.shx" "${base}_text.dbf" "$file"
    rm -f "$file" "$base.shx" "$base.shp" "$base.dbf" "${base}_text.shp" \
                "${base}_text.shx" "${base}_text.dbf"
  }
done
 
# Point Shapefile queue
cd /var/www/acadconverter.chrismichaelis.com/uploads_Point
for file in /var/www/acadconverter.chrismichaelis.com/uploads_Point/*.d*; do
  [[ "$file" =~ .dxf$ || "$file" =~ dwg$ ]] && {
    base=$(basename "$file")
    base=${base%.dwg}
    base=${base%.dxf}
    /var/www/acadconverter.chrismichaelis.com/bin/AutoCADConverter "$file" \
                "$base.shp" point
    [ -e "../outputs/$base.zip" ] && rm -f "../outputs/$base.zip"
    zip "../outputs/$base.zip" "$base.shx" "$base.shp" "$base.dbf" \ 
                "${base}_text.shp" "${base}_text.shx" "${base}_text.dbf"
    zip "../uploads_done/$(date +%s)_$base.zip" "$base.shx" "$base.shp" "$base.dbf" \
                "${base}_text.shp" "${base}_text.shx" "${base}_text.dbf" "$file"
    rm -f "$file" "$base.shx" "$base.shp" "$base.dbf" "${base}_text.shp" \ 
                "${base}_text.shx" "${base}_text.dbf"
  }
done
 
# KML queue
cd /var/www/acadconverter.chrismichaelis.com/uploads_KML
for file in /var/www/acadconverter.chrismichaelis.com/uploads_KML/*.d*; do
  [[ "$file" =~ .dxf$ || "$file" =~ dwg$ ]] && {
    base=$(basename "$file")
    base=${base%.dwg}
    base=${base%.dxf}
    /var/www/acadconverter.chrismichaelis.com/bin/AutoCADConverter "$file" "$base.kml"
    [ -e "../outputs/$base.zip" ] && rm -f "../outputs/$base.zip"
    zip "../outputs/$base.zip" "$base.kml"
    zip "../uploads_done/$(date +%s)_$base.zip" "$base.kml" "$file"
    rm -f "$file" "$base.kml"
  }
done
 
# Text queue
cd /var/www/acadconverter.chrismichaelis.com/uploads_Text
for file in /var/www/acadconverter.chrismichaelis.com/uploads_Text/*.d*; do
  [[ "$file" =~ .dxf$ || "$file" =~ dwg$ ]] && {
    base=$(basename "$file")
    base=${base%.dwg}
    base=${base%.dxf}
    /var/www/acadconverter.chrismichaelis.com/bin/AutoCADConverter "$file" "$base.txt"
    [ -e "../outputs/$base.zip" ] && rm -f "../outputs/$base.zip"
    zip "../outputs/$base.zip" "$base.txt"
    zip "../uploads_done/$(date +%s)_$base.zip" "$base.txt" "$file"
    rm -f "$file" "$base.txt"
  }
done

此网站本身可以是一个简单的文件上载页面。在这种情况下,包含一个上载进度栏是一个很好的增强。名为 Ajax Upload 的开源工具(参阅 参考资料 获得相关链接)就是使用了
XMLHttpRequest 来帮助生成一个更为流畅的上载界面。在 HTML 页,使用 jQuery ready 函数在页面加载后创建这个文件加载程序并传递所选中的输出格式与所加载的文件(参见 清单 9)。此外,还使用了Ajax Upload 工具提供的 PHP upload
脚本,并在该脚本的末尾,修改了 handleUpload 命令来将所上载的文件保存到与将要输出类型相对应的那个目录。这时,可以不必等待计划的 cron
作业的发生,而是使用 exec PHP
函数来启动这个脚本并转换该文件。此 HTML 页会等待片刻直至转换完成,然后再将访客引导到包含了所生成的输出文件的 .zip 文件中。

清单 9. 用来设置文件加载程序的 jQuery ready() 函数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
$(document).ready(function() {
  var uploader = new qq.FileUploader({
    'element': $('#inputFile')[0],
    'action': 'fileuploader.php',
    'params': { 'outputFormat': $('#outputFormat option:selected').attr('value') },
    'onComplete': function(id, file, response) {
      if (!response.success) return;
      // Please Wait
      $('#postLoader').hide()
      $('#postLoader').html('<img src="clock.png" height="210" width="210"><br />
                             Please Wait...');
      $('#postLoader').slideDown();
 
      // for IE compatibility, don't use anonymous function in setTimeout
      setTimeout("showDownload('" + file.replace(".dwg", "").replace(".dxf", "") + "');",
                 5000);
      return true;
    }
  });
 
  $('#outputFormat').change(function() {
    uploader.setParams({
      'outputFormat': $('#outputFormat option:selected').attr('value')
    });
  });
});
 
function showDownload(file) {
  $('#postLoader').slideUp('slow', function() { $('#postLoader').html('<img
        src="download.png" height="48" width="48" align="absmiddle">
        <a href="outputs/' + file + '.zip">Download Ouptut</a>').slideDown(); });
}

上述代码可显著简化这个转换器,实现了方便需要进行文件转换的人们使用的目标。参阅 参考资料 获得该转换器的完整 web
界面的链接,或参阅本文 下载
部分获得这个转换器 Web 页面的完整源代码。

结束语

您可以扩展本示例中的这个简单的文件格式转换器,用它来处理更多几何和地理数据的文件格式。您可以使用本文中所展示的这些库扩展任一软件工具,以便本地处理这些文件格式。除了本文中所展示的 C++ 的用法和语法之外,在很多语言中也有针对这些库的绑定。

下载资源

对开源库使用 AutoCAD 文件格式[转]的更多相关文章

  1. C/C++ 开源库及示例代码

    C/C++ 开源库及示例代码 Table of Contents 说明 1 综合性的库 2 数据结构 & 算法 2.1 容器 2.1.1 标准容器 2.1.2 Lockfree 的容器 2.1 ...

  2. DICOM:DICOM三大开源库对比分析之“数据加载”

    背景: 上一篇博文DICOM:DICOM万能编辑工具之Sante DICOM Editor介绍了DICOM万能编辑工具,在日常使用过程中发现,“只要Sante DICOM Editor打不开的数据,基 ...

  3. PJSIP开源库详解

    PJSIP是一个包含了SIP.SDP.RTP.RTCP.STUN.ICE等协议实现的开源库.它把基于信令协议SIP的多媒体框架和NAT穿透功能整合成高层次.抽象的多媒体通信API,这套API能够很容易 ...

  4. 音视频入门-13-使用开源库生成PNG图片

    * 音视频入门文章目录 * RGB-to-PNG 回顾 上一篇 [手动生成一张PNG图片] 根据 [PNG文件格式详解] 一步一步地手动实现了将 RGB 数据生成了一张 PNG 图片. 有许多开源的 ...

  5. 音视频入门-05-RGB-TO-BMP使用开源库

    * 音视频入门文章目录 * RGB-TO-BMP 回顾 将 RGB 数据转成 BMP 图片: 了解 BMP 文件格式 准备 BMP 文件头信息 准备 BMP 信息头 BMP 存储 RGB 的顺序是 B ...

  6. 【踩坑速记】开源日历控件,顺便全面解析开源库打包发布到Bintray/Jcenter全过程(新),让开源更简单~

    一.写在前面 自使用android studio开始,就被它独特的依赖方式:compile 'com.android.support:appcompat-v7:25.0.1'所深深吸引,自从有了它,麻 ...

  7. Java下好用的开源库推荐

    作者:Jack47 转载请保留作者和原文出处 欢迎关注我的微信公众账号程序员杰克,两边的文章会同步,也可以添加我的RSS订阅源. 本文想介绍下自己在Java下做开发使用到的一些开源的优秀编程库,会不定 ...

  8. 第三方开源库和jar包的区别

    jar包和第三方开源库的根本区别在于,开源库的功能比jar包功能更强大,通过引入库项目可以访问java文件以及该开源库项目下的资源文件,例如图片,layout等文件 jar包中只能放class文件 引 ...

  9. 【转】用JitPack发布开源库时附加文档和源码

    来自:http://www.gcssloop.com/course/jitpack-sources-javadoc 用JitPack发布开源库时附加文档和源码 很早之前写过一篇用JitPack发布An ...

随机推荐

  1. js过滤检测敏感词汇

    html: <textarea rows="10" cols="100" id="myDiv"></textarea> ...

  2. Redis实战(一)

    一.准备 Redis 是一个开源的使用ANSI C 语言编写.支持网络.可基于内存亦可持久化的日志型.Key-Value 数据库.Redis的出现,很大程度补偿了memcached这类key/valu ...

  3. CentOS 7中Nginx1.9.5编译安装教程systemctl启动

    先安装gcc 等 yum -y install gcc gcc-c++ wget 复制代码 .然后装一些库 yum -y install gcc wget automake autoconf libt ...

  4. 基于rsync方式的文件备份

    rsync 是一个快速增量文件传输工具,它可以用于在同一主机备份内部的备分,我们还可以把它作为不同主机网络备份工具之用.本文主要讲述的是如何自架rsync服 务器,以实现文件传输.备份和镜像.相对ta ...

  5. mcnp的重复探测器单元计数-fmesh卡的介绍

    第一步:首先前面是cell surface和material等的定义,忽略,然后写上下面的这些抽样信息等.最后写入fmesh卡的信息定义 第二步:计算上述输入卡,得到结果,显然不在outx,x代表p ...

  6. centos7 更改时区

    Linux 系统(我特指发行版, 没说内核) 下大部分软件的风格就是不会仔细去考虑向后 的兼容性, 比如你上个版本能用这种程序配置, 没准到了下一个版本, 该程序已经不见了. 比如 sysvinit ...

  7. 【BZOJ 3308】 3308: 九月的咖啡店 (费用流|二分图最大权匹配)

    3308: 九月的咖啡店 Time Limit: 30 Sec  Memory Limit: 128 MBSubmit: 244  Solved: 86 Description 深绘里在九份开了一家咖 ...

  8. 【BZOJ 1969】 1969: [Ahoi2005]LANE 航线规划 (树链剖分+线段树)

    1969: [Ahoi2005]LANE 航线规划 Description 对Samuel星球的探险已经取得了非常巨大的成就,于是科学家们将目光投向了Samuel星球所在的星系——一个巨大的由千百万星 ...

  9. JMS介绍:我对JMS的理解和认识

    [ZT]JMS介绍:我对JMS的理解和认识 转自:http://blog.csdn.net/KimmKing/archive/2011/06/30/6577021.aspx,感谢作者KimmKing ...

  10. CSS 笔记——列表表格

    6. 列表表格 -> 列表 (1)list-style 基本语法 list-style : list-style-image || list-style-position || list-sty ...