オープンソース・ライブラリーを活用して AutoCAD ファイル・フォーマットを使用する

DWG ファイル・フォーマットと DXF ファイル・フォーマットを読み取る

エンジニアリング、デザイン、建築、地理、その他の関連分野で、多くの専門家が AutoCAD と AutoCAD フォーマットを使用しています。AutoCAD で生成されるデータをシェープファイルなどの GIS データと組み合わせ、地図上に配置したり、あるいは Google Earth や Google マップの内部で使用したりすると便利な場合がよくあります。この記事では、2 つのオープンソース・ライブラリー、LibreDWG と dxflib について説明します。これらのライブラリーを使用すると、AutoCAD の DXF ファイル・フォーマットと DWG ファイル・フォーマットを容易に利用できるようになります。またこの記事では、KML フォーマットとシェープファイル・フォーマットとして書き出す変換ツールの作成方法と、GDAL ライブラリーを使用して GML フォーマットとシェープファイル・フォーマットを容易に扱う方法についても学びます。

Christopher Michaelis, Developer, Consultant

Christopher Michaelis photoChristopher Michaelis は Web ホスティングと Web アプリケーションを専門とする開発者です。彼は Utah State University と Idaho State University で学び、コンピューター科学と地理情報科学を研究しました。彼の経歴は広範な開発分野に及んでおり、環境モデリング・アプリケーション、教育、糖尿病データ分析、システム管理ツール、Web ホスティング、プラットフォーム統合、ロケーション・ベースのサービスなどがあります。



2011年 10月 07日

はじめに

これまで、開発者や GIS (Geographic Information System: 地理情報システム) の専門家の多くは DXF (Drawing Interchange Format) ファイルや描画ファイル (drawing file: DWG ファイル) を使用することができませんでした。通常、これらの AutoCAD フォーマットを開くためには、Windows と AutoCAD が必要です。いくつかの手軽なオープンソース・ライブラリーを使用すると、費用もかからず、どのようなオペレーティング・システムでもアプリケーションで DXF ファイルと DWG ファイルをそのまま開くことができます。この記事では、これらのファイル・フォーマットをオープンな ESRI シェープファイル・フォーマットまたは KML (Keyhole Markup Language) フォーマットに変換するプログラムを作成します。商用ソフトウェアやオープンソースのソフトウェアでは ESRI シェープファイルを使用し、Google Earth と Google マップでは主に KML を使用しています。


AutoCAD の DWG と LibreDWG

AutoCAD で最も一般的なフォーマットは、ファイル拡張子が .dwg で終わる描画 (drawing) フォーマットです。この DWG は AutoCAD でファイルを保存する場合のデフォルトのフォーマットですが、このフォーマットを読み取れるアプリケーションはほとんどありません。しかし、オープンソースのライブラリー、LibreDWG (「参考文献」を参照) を使用すると、DWG ファイルを読み取ることができます。DWG ファイル・フォーマットは、ファイル内で形状を表す追加ブロックを含む制御ブロックと、文書内にある座標のオフセットを表すモデル・スペースとペーパー・スペース用のブロックとで構成されています。

LibreDWG ライブラリーを使用するためには、文書を開いてファイルを読み取り、次にメインの制御ブロック内の各ブロックに対してループ処理を行います (リスト 1)。

リスト 1. DWG ファイルを開き、メインの制御ブロックにループ処理を行う
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_objectget_next_owned_object によって返されるブロック・オブジェクトのプロパティーを利用します。

リスト 2. get_first_owned_object と get_next_owned_object によってオブジェクトを読み取る
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 ファイルを読み取る場合には、最初から最後までシーケンシャルなフローで読み取ることができます。LibreDWG を C++ で実装する場合には、後でリンカー・エラーが発生しないように extern "C" ブロックの中に dwg.h を含めることが重要です。下記はその一例です。

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

LibreDWG のライブラリーの前提として必要なものは、autoconfswigtexinfopython-dev パッケージと、コンパイラー・パッケージ (Debian または Ubuntu を使用している場合には build-essential) です。このライブラリーをビルドするためには、コマンドラインに以下の内容を入力してライブラリーをダウンロードします。

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

続いて、以下の内容を入力します。

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

AutoCAD の DXF と dxflib

DXF フォーマットは AutoCAD 内部からエクスポートするためのオプションです。そのため、DWG をサポートするアプリケーションよりも DXF フォーマットをサポートするアプリケーションの方が多く、また DXF ファイル・フォーマットの仕様は公開されています (DXF の完全な仕様へのリンクについては「参考文献」を参照)。ただしオープンソースの dxflib ライブラリーを使用すると、DXF フォーマットのファイルを読み取ることができます。LibreDWG の場合とは異なり、dxflib を使って DXF ファイルを読み取るには、シーケンシャルに実行されるコードを作成すればよいわけではありません。実際、dxflib の使い方はイベント駆動型のコードを作成する場合と似ています。

DXF ファイルを開くためには、DL_Dxf オブジェクトの in 関数を呼び出し、DL_CreationAdapter 抽象クラスを継承するクラスへのポインターを渡します。in 関数が実行されると、in 関数は渡されたクラスの中にあるいくつかの関数を呼び出します。そうした関数は何十もありますが (「参考文献」に挙げた「dxflib Programmer’s Guide」へのリンクを参照)、ほとんどの場合、重要な関数はごくわずかであり、通常使用するのは addPointaddLineaddCircle、そして addVertex です。必要な関数のみを実装すればよく、それ以外の関数は無視することができます。リスト 3 は DXF ファイルをロードし、その中から線分のみを読み取る単純な例を示しています。

リスト 3. DXF ファイルをロードして線分のみを読み取る
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 が含まれる場合があります。これらの INSERT を内部に格納し、幾何学フィーチャーが検出されるごとに座標に INSERT を適用する必要があります。同様に、POLYLINE (ポリライン: 複数の頂点を持つ線分) を追加するためには、何らかのデータを格納する必要があります。dxflib ライブラリーは最初に addPolyline を呼び出し、次に線分があることを示します。続いて、その線分の各頂点に対して 1 度ずつ addVertex を呼び出します。最後に、endEntity または endBlock が呼び出されると線分が終了し、その時点で完全な線分となります。その線分を描画することも、新しいフォーマットにエクスポートすることもでき、あるいは他のアクションを実行することもできます。

DXF ライブラリーをビルドしてインストールするためには、単純にコマンドラインに以下の内容を入力します。

./configure && make && sudo make install

すると、strcasecmp に関するエラー・メッセージや、strlen が宣言されていないというエラー・メッセージが表示される場合があります。これは、dxflib ライブラリーは GCC/G++ 4.2 を使用する前提でビルドされており、GCC/G++ はバージョン 4.3 でヘッダー・ファイルの構成が少し変更されていることによるものです。このエラーを修正するためには、他の include 文の近くに、src/dl_writer.h と src/dl_writer_ascii.h の include 文、そして #include <cstring>#include <cstdlib> を追加する必要があります。

注:ダウンロード」セクションにある変換プログラムのソース・コードに含まれる dxflib には、これらの変更が既に加えられています。そのため、dxflib の Web サイトから直接 dxflib をダウンロードした場合にのみ、この変更を適用する必要があります。


KML とプレーン・テキスト/Xerces-C++

Google Earth と Google マップで使用される KML フォーマットは特殊な形式の XML です。そのため、Xerces-C++ XML パーサー (「参考文献」のリンクを参照) などのライブラリーを使用して KML ファイルを処理することができます。これらのフォーマットを読み取る場合には、複雑な構造を処理できるように、Xerces-C++ などの公式ライブラリーを使用することをお勧めします。KML フォーマットを作成する場合には、通常は言語に組み込みの単純な機能を使用してテキスト・ファイルを作成し、適切なテキストを生成するだけで十分です。

基本的な KML ファイルは名前と説明を含む Document セクションで構成されています。また KML ファイルには (形状を論理的に整理するための) フォルダーも 1 つ以上含まれ、各フォルダーの中にプレースマークがあります。プレースマークは実際の形状であり、形状は LineString、Point、Polygon、その他のタイプとして定義することができます。KML ファイルを作成するためには、形状を表すテキストを適切な書式で作成する必要があります (リスト 4)。

リスト 4. 単純な KML ファイル
<?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 ファイルがある場合もあります。KMZ ファイルは単に ZIP で圧縮された KML ファイルにすぎません。KML に関する初心者向けチュートリアルと完全なドキュメントへのリンクは「参考文献」を参照してください。


SRI シェープファイルと GDAL/OGR

シェープファイル・フォーマットは ESRI が公開している商用の (ただしオープンな) バイナリー・データ・フォーマットです (完全な技術的説明へのリンクについては「参考文献」を参照)。GDAL (Geospatial Data Abstraction Layer) の一部であるオープンソースの OGR Simple Feature Library を使用すると、シェープファイルを容易に扱うことができます。この記事で例として挙げる変換プログラムには出力しか必要ありませんが、このライブラリーを利用すると形状データの読み取りも単純になります。データを作成するためには、出力データ・ソースを開き、その中にデータ・レイヤーを作成する必要があります。各形状の非空間データを格納するためにはフィールドを作成することができます。出力データ・ソースを作成した後、そのデータ・ソースに複数の形状 (フィーチャーと呼ばれます) を追加することができます。フィーチャーを作成するためには、点や線分などの幾何形状も作成し、その形状をフィーチャーに関連付ける必要があります。フィーチャーは、すべてのフィールド・データや非空間データ、幾何学データのコンテナーとして機能します。

リスト 5 は、シェープファイルのための新しいデータ・ソースを作成し、そのデータセット内に 1 つの点を作成するためのコードを示しています。「参考文献」には OGR を C++ で読み書きするためのチュートリアルと、OGR クラスの階層構造とドキュメントへのリンクを挙げてあり、そこには OGR の詳細な使い方と OGR Simple Feature Library そのものへのダウンロード・リンクが示されています。ほとんどの Linux ディストリビューションのリポジトリーには、GDAL ライブラリーと、libgdal1-1.7.0libgdal1-dev のようなヘッダー・ファイルが含まれています (バージョンは異なるかもしれません)。

リスト 5. 内部に 1 つの点を持つシェープファイルを作成する
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);
}

ファイル・フォーマット・ライブラリーを実装する

ソフトウェアでファイル・フォーマット・ライブラリーを実装する主な方法には 3 通りの方法があります。1 つ目の方法はアプリケーションでネイティブ実装する方法です。この方法では、コードからライブラリーを直接使用することができなければなりません。それはつまり、どのようなバインディングを利用するかにより、特定の一連の言語を使用してコードを作成する必要がある、ということです。また、強力なリンクも必要であり、そのためライブラリーのバージョンが更新されるとマイナーなエラーが発生する可能性があります。しかしアプリケーションに直接実装すると、ユーザーはそのフォーマットを非常に使いやすくなります。

2 つ目の方法は、必要なファイル・フォーマットをサポートするプラグインまたは拡張機能をアプリケーション用に作成する方法です。この方法では、ある程度ファイル・フォーマット・ライブラリーとアプリケーション・コードとを分離することができます。アプリケーションに既にプラグイン・フレームワークがある場合には、この方法が適切な選択肢となる可能性があります。例えば、一般的なデスクトップ GIS アプリケーションである Quantum GIS はプラグイン・アーキテクチャーを使用しています。そうしたファイル・フォーマット用のプラグインを利用することで、区切られたテキスト・ファイルをアプリケーションの中で直接使用できるようになります。

3 つの中で最後の、そして最も容易な方法は、スタンドアロンの変換プログラムを作成して複数ファイル・フォーマット間の変換を行う方法です。この方法では、ユーザーにとっては必要なステップが増えますが、最終的にデータをどうするかによらず、変換プログラムを再利用できるというメリットがあります。


コマンドラインで DXF/DWG を KML/シェープファイルに変換する

この例では、ファイル・フォーマット変換プログラムを作成するための方法として、他の方法よりも容易で再利用しやすい方法を採用します。ここで作成する変換プログラムの目標は、他のフォーマットを扱うための拡張が容易なこと、また特定の入力フォーマットまたは出力フォーマットを扱うためのロジックが可能な限り分離されていることです。これらの目標から、2 つの抽象クラス、InputFormatOutputFormat を定義します (リスト 6 を参照)。

リスト 6. 抽象クラスである InputFormat と OutputFormat の定義
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

どのようなフォーマットを実装する場合であれ、そのフォーマットはこの 2 つのクラスのうちの 1 つを継承する必要があります。こうすることで、変換プログラムの main 関数とエントリー・ポイントは、どちらのクラスをインスタンス化するのかのみを判断すればよくなります。入力ファイルと出力ファイルが定義されると、以下の 1 行のコードで変換を行うことができます。

input->readFeaturesInto(output);

その結果、main 関数 (リスト 7) の役割は適切な入力フォーマットと出力フォーマットとをインスタンス化することのみとなり、readFeaturesInto 関数を呼び出すことになります。

リスト 7. AutoCAD 変換プログラムの main() 関数
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 クラスには、InputFormat クラスの場合よりも多くの関数、つまりさまざまなタイプの幾何形状を追加するための関数が必要です。すべての引数を整数ではなく倍精度の値として指定できるように、また z 軸を指定できるように、点と線分のための独自のクラスを定義します。

ESRI シェープファイル・フォーマットには重要な制約が 1 つあり、このフォーマットでは 1 つのタイプの形状しか格納することができません (線分のみ、点のみ、ポリゴンのみ、など)。この記事で説明した他のフォーマット (DXF、DWG、KML) には、この制約はありません。OutputFormatSHP クラスの中では、作成しているシェープファイルがどんな種類なのかを調べ、シェープファイルのタイプが適切ではない場合には、そのファイルを最善の方法で処理します。点のシェープファイルを作成する際にポリライン形状を追加するように促された場合には、各頂点を個々の点として追加します。線分のシェープファイルを作成する際に点を追加するように促された場合には、形状が無視されたことを示す警告を標準エラーに書き込みます。また DXF ファイル・フォーマットと DWG ファイル・フォーマットは円弧と円もサポートしていますが、円または円弧を近似する複数の小さな線分セグメントを作成することで、出力フォーマットでは円または円弧を近似することができます。

サンプル・アプリケーションをコンパイルすると、dwg.h に関連するエラー・メッセージが表示される場合があります。dxflib ライブラリーも LibreDWG ライブラリーも、THICKNESS をグローバルとして定義しています。そこで、dwg.h がインストールされた場所 (例えば /usr/local/include/dwg.h など) で dwg.h ファイルを編集し、THICKNESS 定数の名前を変更するか、あるいは最後にアンダーバー (_) を追加します。(このサンプル・コードでは、実際には THICKNESS は使われていません。)

注: この記事で例として取り上げたファイル・フォーマット変換プログラムの完全なソース・コードについては、「ダウンロード」セクションを参照してください。


Web サイトの中に変換プログラムをラップする

この時点で、完全に機能するコマンドラインのファイル・フォーマット変換プログラムができました。しかし、これらのファイルの読み取り方法について質問するユーザーの大部分は、コマンドラインを使うことに慣れていないか、あるいは自分でソースから変換プログラムをビルドすることができないことが普通です。そのため、このツールへの単純な Web ベースのゲートウェイを提供するのが賢明です。そうすれば、1 つか 2 つのファイルのちょっとした変換が必要なユーザーは手軽に変換を行うことができます。また同時に、ディスク上の特定のディレクトリーにファイルが追加された場合にファイルを自動変換するシステムを作成することができます。このシステムは FTP でやり取りする場合や CIFS によるファイル共有でアクセスする場合に便利です。

まず、さまざまなタイプのシェープファイル出力フォーマット用に、以下に示すいくつかのディレクトリーを作成します。

  • 出力フォーマットが KML 用とテキスト用の uploads_KML と uploads_Text
  • さまざまなタイプのシェープファイル出力フォーマット用の uploads_Point、uploads_Polyline、uploads_Polygon

規則としては、これらの場所にアップロードされる DXF ファイルまたは DWG ファイルはすべて、ディレクトリー名が示すフォーマットに変換されます。出力は outputs という別のディレクトリーに配置する必要があります。

このような設定をしたら、単純な BASH シェル・スクリプト (リスト 8 を参照) を作成することができます。このスクリプトにより、この場所に DWG ファイルと DXF ファイルがあるかどうかをチェックし、適切なコマンドライン引数を使用して変換プログラムを起動します。また、後でデバッグをしやすいように、このスクリプトはアップロードされた入力と生成された出力のすべてを一括してタイムスタンプ付きの .zip ファイルに保存し、管理用として使用できるようにします。このスクリプトは、cron ジョブとして定期的に実行されるように設定することや、FTP や CIFS (Windows のファイル共有) の自動変換プログラムとして使用されるように設定することもできます。

リスト 8. 変換対象のファイルがあるかどうかをチェックし、該当するファイルがあれば、そのファイルを変換プログラムに渡すためのBASH スクリプト
#!/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

Web サイト自体は単純にファイルをアップロードするページにすることができます。この場合、アップロード状況を示すプログレス・バーをページに含めると、望ましい機能強化が行われることになります。Ajax Upload というオープンソース・ツール (「参考文献」のリンクを参照) は、XMLHttpRequest を使用して柔軟なアップロード・インターフェースを作成することができます。この HTML ページでは、ページがロードされると jQuery の ready 関数を使用してファイル・アップローダーを作成し、アップロードされたファイルと共に、選択された出力フォーマットを渡します (リスト 9 を参照)。ここでは Ajax Upload ツールに用意されている平凡な PHP アップロード・スクリプトを使用しており、そのスクリプトの最後で handleUpload コマンドを変更し、要求された出力タイプ用の適切なディレクトリーに、アップロードされたファイルが格納されるようにしています。また、スケジューリングされた cron ジョブが実行されるのを待たなくても、PHP の exec 関数を使用してスクリプトを起動し、ファイルを変換することができます。HTML ページは変換が完了するまでしばらく待ち、変換が完了すると、変換結果の出力ファイルが含まれる .zip ファイルを訪問者が取得できるようにします。

リスト 9. jQuery の ready() 関数を使用してファイル・アップローダーを設定する
$(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 file format converter source codeAutoCADConverter-SourceCode.zip1.6MB
Converter website source codeConverterWebsite-SourceCode.zip0.36MB

参考文献

学ぶために

製品や技術を入手するために

  • dxflib ライブラリーの Web サイトには、ソース・コードのダウンロードやドキュメントが用意されています。
  • GNU LibreDWG の Web サイト: ソース・コードやドキュメントが用意されています。
  • Xerces-C++ XML パーサー: (GML や KML などの派生フォーマットを含む) 複雑な XML 文書を構文解析するための、信頼性の高い優れた手段を利用してください。
  • GDAL のダウンロード・ページ: GDAL の一部、OGR のソース・コードを入手することができます。
  • Ajax Upload: このツールを入手すると、HTTP でファイルをアップロードする際、XmlHTTPRequest を使用して見やすいアップロード・プログレス・バーを表示することができます。
  • C/C++ 開発: 最適化のために、AIXLinux のための IBM の XL C/C++ を使用することができます。

議論するために

コメント

developerWorks: サイン・イン

必須フィールドは(*)で示されます。


IBM ID が必要ですか?
IBM IDをお忘れですか?


パスワードをお忘れですか?
パスワードの変更

「送信する」をクリックすることにより、お客様は developerWorks のご使用条件に同意したことになります。 ご使用条件を読む

 


お客様が developerWorks に初めてサインインすると、お客様のプロフィールが作成されます。会社名を非表示とする選択を行わない限り、プロフィール内の情報(名前、国/地域や会社名)は公開され、投稿するコンテンツと一緒に表示されますが、いつでもこれらの情報を更新できます。

送信されたすべての情報は安全です。

ディスプレイ・ネームを選択してください



developerWorks に初めてサインインするとプロフィールが作成されますので、その際にディスプレイ・ネームを選択する必要があります。ディスプレイ・ネームは、お客様が developerWorks に投稿するコンテンツと一緒に表示されます。

ディスプレイ・ネームは、3文字から31文字の範囲で指定し、かつ developerWorks コミュニティーでユニークである必要があります。また、プライバシー上の理由でお客様の電子メール・アドレスは使用しないでください。

必須フィールドは(*)で示されます。

3文字から31文字の範囲で指定し

「送信する」をクリックすることにより、お客様は developerWorks のご使用条件に同意したことになります。 ご使用条件を読む

 


送信されたすべての情報は安全です。


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=60
Zone=Open source
ArticleID=762688
ArticleTitle=オープンソース・ライブラリーを活用して AutoCAD ファイル・フォーマットを使用する
publish-date=10072011