これまで、開発者や 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 で終わる描画 (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_object と get_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 のライブラリーの前提として必要なものは、autoconf、swig、texinfo、python-dev パッケージと、コンパイラー・パッケージ (Debian または Ubuntu を使用している場合には build-essential) です。このライブラリーをビルドするためには、コマンドラインに以下の内容を入力してライブラリーをダウンロードします。
git clone git://git.sv.gnu.org/libredwg.git |
続いて、以下の内容を入力します。
./autogen.sh && ./configure && make && sudo make install |
DXF フォーマットは AutoCAD 内部からエクスポートするためのオプションです。そのため、DWG をサポートするアプリケーションよりも DXF
フォーマットをサポートするアプリケーションの方が多く、また DXF ファイル・フォーマットの仕様は公開されています (DXF の完全な仕様へのリンクについては「参考文献」を参照)。ただしオープンソースの dxflib
ライブラリーを使用すると、DXF フォーマットのファイルを読み取ることができます。LibreDWG の場合とは異なり、dxflib を使って DXF
ファイルを読み取るには、シーケンシャルに実行されるコードを作成すればよいわけではありません。実際、dxflib の使い方はイベント駆動型のコードを作成する場合と似ています。
DXF ファイルを開くためには、DL_Dxf オブジェクトの in
関数を呼び出し、DL_CreationAdapter 抽象クラスを継承するクラスへのポインターを渡します。in
関数が実行されると、in 関数は渡されたクラスの中にあるいくつかの関数を呼び出します。そうした関数は何十もありますが
(「参考文献」に挙げた「dxflib Programmer’s Guide」へのリンクを参照)、ほとんどの場合、重要な関数はごくわずかであり、通常使用するのは addPoint、addLine、addCircle、そして 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 をダウンロードした場合にのみ、この変更を適用する必要があります。
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 に関する初心者向けチュートリアルと完全なドキュメントへのリンクは「参考文献」を参照してください。
シェープファイル・フォーマットは 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.0 や libgdal1-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
つの抽象クラス、InputFormat と OutputFormat を定義します (リスト 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 ベースのゲートウェイを提供するのが賢明です。そうすれば、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 code | AutoCADConverter-SourceCode.zip | 1.6MB | HTTP |
| Converter website source code | ConverterWebsite-SourceCode.zip | 0.36MB | HTTP |
学ぶために
- 「Linux
上のアプリケーションで GDAL を使用して地理空間データを扱う」(Christopher Michaelis 著、developerWorks、2011年2月): この記事で使用した概念や用語について、他の背景も知ることができます。
- DXF Reference: 問題のトラブルシューティングや独自にデコードを行う場合に役立ちます。
- dxflib
プログラマーズ・ガイド: 実用的なアプリケーションに
dxflibを使用するための方法を学ぶことができます。 - Google KML チュートリアル: KML について学んでください。
- KML のドキュメント: 他の高度な機能について学んでください。
- ESRI Shapefile
Technical Description: サードパーティーのライブラリーを使用することなく、独自にシェープファイルを構文解析するためのコードを作成する方法について学んでください。
- OGR
API Tutorial: OGR/GDAL を使用してシェープファイルを扱うための方法の詳細について学んでください。
- OGR
クラスの階層構造とドキュメント: OGR Simple Feature Library のすべての機能を学んでください。
- AutoCAD 変換の Web ページ: この記事で説明した Web サイトと変換ツールについて学んでください。
- developerWorks の Open source ゾーン: オープンソース技術を使用した開発や、IBM 製品でオープンソース技術を使用するためのハウ・ツー情報やツール、プロジェクトの更新情報など、豊富な情報が用意されています。
- developerWorks
の Technical events and webcasts: 最新情報を入手してください。
- Technology
bookstore: この記事や他の技術的な話題に関する本が豊富に取り揃えられています。
- developerWorks
podcasts: ソフトウェア開発者のための興味深いインタビューや議論を聞いてください。
製品や技術を入手するために
dxflibライブラリーの Web サイトには、ソース・コードのダウンロードやドキュメントが用意されています。- GNU LibreDWG の Web サイト: ソース・コードやドキュメントが用意されています。
- Xerces-C++ XML パーサー: (GML や KML などの派生フォーマットを含む) 複雑な XML 文書を構文解析するための、信頼性の高い優れた手段を利用してください。
- GDAL のダウンロード・ページ: GDAL の一部、OGR のソース・コードを入手することができます。
- Ajax Upload: このツールを入手すると、HTTP
でファイルをアップロードする際、
XmlHTTPRequestを使用して見やすいアップロード・プログレス・バーを表示することができます。 - C/C++ 開発: 最適化のために、AIX と Linux のための IBM の XL C/C++ を使用することができます。
議論するために
- developerWorks blogs: これらのブログから developerWorks のコミュニティーに加わってください。
