Using AutoCAD file formats with open source libraries

Reading DWG and DXF file formats

Many experts use AutoCAD and its formats in engineering, design, architecture, geography, and related fields. It's often useful to combine the data that AutoCAD produces with GIS data such as shapefiles for placement on maps or for use inside Google Earth or Google Maps. Learn about two open source libraries—LibreDWG and dxflib—that make the AutoCAD DXF and DWG file formats more accessible. You also create a translator tool that will write to KML and shapefile formats and use the GDAL library to facilitate working with GML and shapefile formats.

Share:

Christopher Michaelis, Developer, Consultant

Christopher Michaelis photoChristopher Michaelis is a developer specializing in web hosting and web applications. He attended Utah State University and Idaho State University, studying computer science and geographic information science. His experience covers a wide range of development, including applications for environmental modeling, education, diabetes data analysis, systems administration tools, web hosting, platform integration, and location-based services.



30 August 2011

Also available in Chinese Japanese Spanish

Introduction

Many developers and geographic information system (GIS) professionals have been unable to use a Drawing Interchange Format (DXF) or "drawing" (DWG) file. These AutoCAD formats generally require that you have Windows® and a copy of AutoCAD to open them. Using a few handy open source libraries, you can make your applications able to natively read DXF and DWG files on any operating system at no cost. In this article, you build a converter to translate these file formats into the more open ESRI shapefile or keyhole markup language (KML) formats. Commercial and open source software use ESRI Shapefiles, while Google Earth and Google Maps mainly use KML.


AutoCAD DWG and LibreDWG

The most common AutoCAD format is the "drawing" format, with a file extension ending in .dwg. Few software applications can read this format, which is the default when saving files in AutoCAD. However, you can use the open source library LibreDWG (see Resources) to read these files. The file format consists of a control block, which contains additional blocks representing the shapes in the file, and blocks for model space and paper space, which represent offsets for the coordinates that lie within the document.

You use the library by opening the document and reading the file, followed by looping through each block in the main control block, as shown in Listing 1.

Listing 1. Opening a DWG file and looping through the main control block
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);

Each block can represent any of several types of geometry: a line, circle, arc, text anchored to a location, or an insert (an offset to be applied to later blocks). You handle each in turn by accessing properties on the block objects that get_first_owned_object and get_next_owned_object return, as illustrated in Listing 2.

Listing 2. Reading objects with get_first_owned_object and 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);
  }
}

In this way, reading a DWG file with LibreDWG is a sequential flow from start to finish. When implementing LibreDWG in C++, it's important that you include dwg.h inside an extern "C" block to avoid encountering linker errors later. Here's an example:

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

Prerequisites for this library are the autoconf, swig, texinfo, and python-dev packages as well as compiler packages (build-essential if using Debian or Ubuntu). You can build the library by downloading it by entering the following on the command line:

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

. . . followed by:

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

AutoCAD DXF and dxflib

The DXF format is an export option from within AutoCAD. As such, more applications support it than support DWG, and the file format specification is published (see Resources for a link to the full DXF specification). You can, however, read these files using the open source dxflib library. Unlike LibreDWG, reading a DXF file is driven less by sequential coding of your own. In fact, using dxflib feels something like writing event-driven code.

You open the file by calling the in function of the DL_Dxf object and passing a pointer to a class that inherits from the DL_CreationAdapter abstract class. As the in function runs, it calls several functions in the class passed to it. There are dozens of such functions (see the DXFLib Programmer's Guide link in Resources), but you'll care only about a handful in most cases—typically, addPoint, addLine, addCircle, and addVertex. You only need to implement those functions you care about; the rest you can omit. Listing 3 shows a simple example of loading a DXF file and reading only lines from it.

Listing 3. Loading a DXF file and reading only lines
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);
}

Like DWG, the DXF format can contain inserts, which represent offsets to be applied to geometric features encountered after the insert. These inserts must be stored internally and applied to coordinates as they are encountered. Similarly, adding polylines (lines with multiple vertices) requires storing some data. The library first calls addPolyline, indicating that a line is coming; it then calls addVertex once for each vertex of the line. Finally, the line is ended when endEntity or endBlock is called, at which point you have the complete line and can render it, export it to a new format, or take other action.

You can build and install the DXF library by simply entering the following on the command line:

./configure && make && sudo make install

You may receive error messages about strcasecmp as well as about strlen being undeclared. The dxflib library was built to use GCC/G++ 4.2, and in version 4.3, some reorganization of header files took place. Fixing this error requires adding a few includes in src/dl_writer.h and src/dl_writer_ascii.h as well as in #include <cstring> and #include <cstdlib> near the other includes.

Note: These changes were already made to the copy of dxflib included with the converter source code available in Download, so this change applies only if you download dxflib directly from the dxflib website.


KML and plain text/Xerces-C++

The KML format that Google Earth and Google Maps use is a specialized form of XML; as such, you can use libraries such as Xerces-C++ XML Parser (see Resources for a link) to work with KML files. When reading these formats, formal libraries like Xerces-C++ are advisable for handling more complex structures that you may encounter. When writing, it is usually sufficient to use simple built-in language functionality to write text files, and then generate the appropriate text yourself.

A basic KML file consists of a Document section that contains a name and description. The file may also contain one or more folders (used to organize shapes logically), and inside each folder are placemarks. The placemarks are the actual shape, which you can define as LineString, Point, Polygon, or other types. Writing a KML file requires writing properly formatted text representing the shapes, as shown in Listing 4.

Listing 4. A simple KML file
<?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>

You may also encounter KMZ files, which are just KML files compressed with ZIP compression. See Resources for a link to a beginner tutorial on KML and complete KML documentation.


ESRI Shapefile and GDAL/OGR

The shapefile format is a commercial (but open) binary data format published by ESRI (see Resources for a link to the full technical description). You can use the open source OGR Simple Feature Library—part of the Geospatial Data Abstraction Layer (GDAL)—to access these files easily. In your example converter, you only need output, but this library also simplifies reading shape data. Writing data requires opening an output data source and creating a data layer within it. You can create fields to store nonspatial data for each shape. After the output data source has been created, you can add multiple shapes (called features) to the data source. Creating a feature requires that you also create a geometry, such as a point or line, and associate it with the feature. The feature acts as a container for all field/nonspatial and geometric data.

Listing 5 shows the code for creating a new shapefile data source and a single point inside the dataset. See the OGR C++ read/write tutorial and the OGR class hierarchy and documentation links in Resources for more information on using OGR and for a download link to the library itself. Most Linux® distribution repositories include the GDAL library and header files as libgdal1-1.7.0 and libgdal1-dev (the version may vary).

Listing 5. Creating a shapefile with a single point inside it
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);
}

Implementing file format libraries

There are three main ways to implement a file format library in software. The first way— native implementation in applications— requires that you be able to use the libraries from your code directly, which means that you may have to write your code in a particular set of languages, depending on binding availability. It also requires some strong linkage, which may cause minor errors when library versions are updated. However, direct implementation in your application does make using the format feel much smoother to users.

Another method is to write a plug-in or extension for your application that provides support for the desired file formats. This method gives you some degree of separation between a file format library and your application code. If your application already has a plug-in framework, this may be a good option. For example, Quantum GIS, a common desktop GIS application, uses a plug-in architecture; one such plug-in allows you to use delimited text files directly in the application.

The last and simplest method of all— a stand-alone converter— can be created to translate between two or more file formats. This technique has the advantage of being reusable regardless of your end goals for the data at the cost of adding another step for users.


A command-line translator for DXF/DWG to KML/Shapefile

In this example, you take the simpler and more reusable route of creating a file format translator. Your goal is to create a converter that is easy to extend to handle additional formats and that keeps logic for any particular input or output format as separate as possible. These goals lead to the definition of two abstract classes: InputFormat and OutputFormat (see Listing 6).

Listing 6. InputFormat and OutputFormat abstract class definitions
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

Any format you implement must inherit from one of these classes. In this way, the main function and entry point of the program can deal exclusively with determining which class to instantiate. After an input file and output file have been defined, conversion can take place with a single line of code:

input->readFeaturesInto(output);

The role of the main function (see Listing 7) then becomes solely instantiating the proper input and output formats, culminating in the readFeaturesInto function call.

Listing 7. The main() function of the AutoCAD converter
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;
}

The only required function in the InputFormat class is readFeaturesInto, which leaves it open to the individual input format how it actually wants to provide features (shapes) to the provided output class. The OutputFormat class has a few more required functions, however— functions to add various types of geometric shapes. You define your own classes for point and line so that you can provide all arguments as double values instead of integers and so that you can provide a z-axis.

The ESRI Shapefile format has one important limitation; it can only store shapes of a single type (for example, only lines, only points, or only polygons). No other formats discussed in this article— DXF, DWG, and KML— have this restriction. In the OutputFormatSHP class, you check to see what kind of shapefile you're creating; if it's not the right type, you handle it the best you can. While creating a point shapefile, when you are prompted to add a polyline shape, add each vertex as an individual point. While creating a line shapefile, if you are prompted to add a point, a warning is written to standard error indicating that the shape was ignored. The DXF and DWG file formats also support arcs and circles, which you can approximate in your output formats by creating multiple small line segments approximating the circle or arc.

When compiling the example application, you may receive error messages relating to dwg.h. Both the dxflib and LibreDWG libraries define the global definition THICKNESS, so edit the dwg.h file in its installed location (for example, /usr/local/include/dwg.h) and change the name of the THICKNESS constant or add an underscore (_) at the end. (This is not actually used in this example code.)

Note: See the Download section for the full source code of this example file format translator.


Wrapping the translator in a website

At this point, you have a fully functional command-line translator for file formats. However, most of the users who typically ask how to read these files are neither command-line savvy nor capable of building the converter from source themselves. It makes sense, then, to provide a simple web-based gateway to this tool, so that people needing quick conversion of one or two files can easily do so. At the same time, you can create a system in which files are automatically converted when added to a particular directory on disk—useful for FTP interaction or for access through a CIFS file share.

First, create a handful of directories for the various shapefile output format types:

  • uploads_KML and uploads_Text for KML and text output formats
  • uploads_Point, uploads_Polyline, and uploads_Polygon for the various types of shapefile output formats

The convention will be that any DXF or DWG file uploaded into any of these locations will be converted into the format the directory name implies. Outputs should be placed in another directory, called outputs.

Given such a setup, you can construct a simple BASH shell script (see Listing 8) to check for DWG and DXF files in this location and trigger the converter with the appropriate command-line arguments. To aid in future debugging, this script can also save all uploaded inputs together with generated outputs into a time-stamped .zip file for administrative use. You can set this script up to run periodically as a cron job or for FTP and CIFS (Windows file share) use as an automatic converter.

Listing 8. BASH script to check for files pending conversion and pass them to the converter
#!/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

The website itself can be a simple file upload page. In this case, including an upload progress bar would be a good enhancement. An open source tool called Ajax Upload (see Resources for a link) uses XMLHttpRequest to help produce a more fluid upload interface. In your HTML page, use the jQuery ready function to create the file uploader once the page has loaded and pass the selected output format along with the uploaded file (see Listing 9). The stock PHP upload script provided with the Ajax Upload tool is used, and at the end of that script, the handleUpload command is altered to save the uploaded file into the correct directory for the desired output type. Rather than wait for the scheduled cron job to take place, you then use the exec PHP function to start the script and convert the file. The HTML page waits a few moments for conversion to finish, and then directs the visitor to the generated .zip file containing the produced output files.

Listing 9. jQuery ready() function to set up the file uploader
$(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(); });
}

This code simplifies the converter considerably, accomplishing the goal of making it easily accessible to anyone who needs a file converted. See Resources for a link to the completed web interface to the converter, or see the Download section for complete source code to the converter web page.


Conclusion

You can extend the simple file format converter in this example to handle more file formats for geometric and geographic data. You can use the libraries demonstrated here to extend any software tool to natively handle these file formats. Bindings for these libraries are available in multiple languages beyond the C++ usage and syntax demonstrated here.


Downloads

DescriptionNameSize
AutoCAD file format converter source codeAutoCADConverter-SourceCode.zip1.6MB
Converter website source codeConverterWebsite-SourceCode.zip0.36MB

Resources

Learn

Get products and technologies

  • The dxflib library website includes source code downloads and documentation.
  • GNU LibreDWG website: Find source code as well as documentation.
  • Xerces-C++ XML Parser: Use this excellent and reliable way to parse complex XML documents (including derivatives like GML and KML).
  • GDAL download page: Get the source code for OGR—a part of GDAL—.
  • Ajax Upload: Get this tool that uses XmlHTTPRequest to display friendly upload progress bars on HTTP file uploads.
  • C/C++ development: You might also be interested in the optimization you can achieve with IBM's XL C/C++ for AIX and Linux.

Discuss

Comments

developerWorks: Sign in

Required fields are indicated with an asterisk (*).


Need an IBM ID?
Forgot your IBM ID?


Forgot your password?
Change your password

By clicking Submit, you agree to the developerWorks terms of use.

 


The first time you sign into developerWorks, a profile is created for you. Information in your profile (your name, country/region, and company name) is displayed to the public and will accompany any content you post, unless you opt to hide your company name. You may update your IBM account at any time.

All information submitted is secure.

Choose your display name



The first time you sign in to developerWorks, a profile is created for you, so you need to choose a display name. Your display name accompanies the content you post on developerWorks.

Please choose a display name between 3-31 characters. Your display name must be unique in the developerWorks community and should not be your email address for privacy reasons.

Required fields are indicated with an asterisk (*).

(Must be between 3 – 31 characters.)

By clicking Submit, you agree to the developerWorks terms of use.

 


All information submitted is secure.

Dig deeper into Open source on developerWorks


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=Open source
ArticleID=754324
ArticleTitle=Using AutoCAD file formats with open source libraries
publish-date=08302011