A surveyor sends you a DWG with what looks like UTM coordinates. You open it in QGIS, the layer appears 100 kilometers from where you expected, and when you zoom to it the geometry is microscopic — a thirty-meter parcel that displays as a 30-millimeter sliver. You set the project CRS to the right UTM zone. Nothing changes. You build a custom CRS in metric units. Nothing changes. You ask the surveyor and they swear the file is correct in AutoCAD.
Both of you are right. The DWG is correct in AutoCAD, and the file is unusable in QGIS as-is. The problem is in two layers most GIS workflows never make explicit: DWG has no CRS at all, and the unit interpretation depends on a header variable that a CAD-trained surveyor and a GIS-trained analyst read differently. This post explains what is happening, why fiddling with QGIS CRS settings cannot fix it, and the two clean repairs depending on which side of the pipeline you control.
DWG Does Not Carry a CRS
Shapefiles ship with a .prj sidecar. GeoTIFFs embed CRS information in TIFF tags or a .prj. GeoPackage stores CRS in a metadata table. DWG stores none of this. The DWG specification — both the published flavor and the AutoCAD-internal one used by recent versions — has no field for “this drawing is in EPSG:32633.” The closest thing is the INSUNITS header variable, which says “the numbers in this drawing are in this unit.”
INSUNITS is an enumeration:
- 0: Unspecified (worst case — completely ambiguous)
- 1: Inches
- 2: Feet
- 4: Millimeters
- 5: Centimeters
- 6: Meters
- 8: Microinches
- 10: Yards
- 12: Decimeters
- 13: Decameters
- 14: Hectometers
- 15: Kilometers
That’s it. There is no spheroid, no datum, no projection, no false easting or northing. A point at coordinate (508220.5, 5414912.3) is just a pair of numbers with a unit tag. The unit tag tells you “these are meters” or “these are millimeters.” It does not tell you those meters are UTM 33N northing. You — or the receiving software — supply that information out-of-band.
GDAL (which QGIS uses for DWG reading via the libdwg driver, the AutoCAD ODA driver, or the DXF fallback) reads INSUNITS, treats the geometry as raw numbers in that unit, and hands them to QGIS. QGIS shows them in the canvas using whatever CRS you’ve assigned to the layer. There is no protocol layer between DWG and QGIS that says “this should be UTM 33N.” That has to come from you.
The Most Common Failure: UTM in Millimeters
Here is the specific situation that produces the “100 km from where I expected” symptom. The surveyor in AutoCAD has set their drawing to millimeters because the company’s title blocks, dimensions, and detail drawings all assume mm. They then place geometry at real-world UTM coordinates — but as millimeters. The eastings might be 507220500 instead of 507220.5. The northings might be 5414912300 instead of 5414912.3. To AutoCAD, this is fine; the geometry plots at the correct full-scale survey coordinates because the units are mm and 507220500 mm is 507.22 km from the origin, which is correct UTM easting.
You receive the DWG. QGIS reads INSUNITS = 4 (millimeters), so GDAL interprets coordinates as millimeters. You assign the layer to EPSG:32633 (UTM 33N, which uses meters). QGIS treats 507220500 as 507,220,500 meters, which is well past the moon. Hence the layer being 100 km — actually 100,000 km — from where you expected. The other failure mode is when you assign EPSG:32633 and zoom to the layer extent: the extent is correct in millimeters but you’ve told QGIS those numbers are meters, so the geometry shows up at huge offset and the survey parcel becomes a sliver because it’s now drawn at 1/1000th of its real width relative to the canvas extent.
Why Custom CRS Cannot Fix It
The instinct is to define a custom CRS in QGIS that says “like UTM 33N but in millimeters.” It does not work. Here is why.
A CRS in PROJ defines a transformation between geographic coordinates (longitude, latitude) and projected coordinates. The unit at the projected end is fixed at definition time: a meter, a foot, a US survey foot. The unit is not a reinterpretation knob. When you tell QGIS “this layer is in EPSG:32633,” QGIS does not rescale the numbers — it uses them directly as UTM 33N easting and northing in meters.
You can in fact define a custom PROJ string with a different unit (+units=mm), but the QGIS DWG driver does not consult the project CRS to choose a scale; it consults INSUNITS from the header. The driver returns geometry already in the layer’s native unit. Setting a custom CRS would only matter for on-the-fly reprojection of geometry that was already correct in some unit. If the geometry is in mm and you tell QGIS the layer CRS is “UTM 33N in mm,” the layer’s geometry stays at 507220500 and now QGIS knows that’s mm. The on-the-fly transform to “the same projection in m” still leaves you with a ratio problem — between the layer extent (in mm) and the rest of the project (in m). Anything else in the project, basemaps and other layers, will be 1000x off.
Custom CRS is not the place to fix unit confusion. The geometry has to be rescaled.
The Right Fix Depends on Where You Are
There are two clean repair points, and a wrong one to avoid.
Wrong: rescale in QGIS using the affine transform plugin or a math expression. It works once. It produces a new layer in the right unit. It loses the original drawing structure (blocks, layers, line-type scales, hatch patterns) because you’ve routed everything through GDAL’s vector layer model. If your output is just a shapefile or GeoPackage, this is fine. If you need to keep the result as a DWG and send it back, you’ve stripped the CAD richness in the round trip.
Right (you control AutoCAD): use -DWGUNITS. AutoCAD ships an undocumented-on-purpose command, -DWGUNITS (the dash prefix is mandatory; without it the command is rejected). It walks you through changing the drawing’s INSUNITS and rescales every entity to match. Run it on the original DWG before sending: switch the drawing from millimeters to meters, AutoCAD divides every coordinate and length by 1000, and the file you ship to the GIS team has INSUNITS = 6 and real meter coordinates. Open in QGIS, assign EPSG:32633, and the geometry lines up with everything else.
The procedure inside AutoCAD:
Command: -DWGUNITS
Drawing units:
1. Inches
2. Feet
3. Miles
4. Millimeters
5. Centimeters
6. Meters
7. Kilometers
Drawing unit <4>: 6
Scale objects from other drawings upon insert? [Yes/No] <Y>: Y
Scale objects in current drawing to reflect change in units? [Yes/No] <Y>: Y
Include objects in Paper Space? [Yes/No] <Y>: Y
The third prompt is the important one. Saying yes here is what triggers the rescale. Saying no leaves the geometry numerically the same but changes the header — exactly what you don’t want, because then the numbers are 1000x larger than the new unit suggests.
There is one footgun. If the drawing contains dynamic blocks with internal scaling tied to drawing units, -DWGUNITS can scale them incorrectly. Autodesk has a knowledge-base article specifically about this. For most surveying DWGs the dynamic block content is minimal or absent, but if your drawing has parametric title blocks or dimension styles with scale factors, audit them after the conversion.
Right (you control the GIS side): use ogr2ogr with a manual scale. When you cannot change the source — the surveyor is upstream, the file is a deliverable, the company process freezes the DWG — convert it once on your end with explicit unit handling. ogr2ogr lets you assign the source CRS and write a target file in the right unit:
ogr2ogr -f "GPKG" output.gpkg input.dwg \
-s_srs "+proj=utm +zone=33 +datum=WGS84 +units=mm +no_defs" \
-t_srs "EPSG:32633" \
-lco GEOMETRY_NAME=geom
The source SRS includes +units=mm, telling PROJ to treat the raw coordinates as millimeters in UTM 33N. The target SRS is the same projection in meters. PROJ rescales the coordinates during the reprojection. The output GeoPackage opens in QGIS with EPSG:32633 set automatically, and the geometry is in the right place at the right scale.
Note: this works because PROJ supports +units= in custom strings even though no published EPSG code exists for “UTM 33N in millimeters.” You’re synthesizing a CRS string for the read step. The target is a real EPSG code, so the output is interoperable.
The same pattern handles the inch/foot variants. A US-surveyed DWG might have eastings in US survey feet under the assumption of EPSG:6342 or similar. The ogr2ogr command becomes +units=us-ft on the source side, with the target as the meter-based equivalent.
How to Tell Which Unit the DWG Is Actually In
You don’t have to guess. The easiest check is ogrinfo, which is shipped with GDAL and therefore with QGIS:
ogrinfo -ro -al -so input.dwg | head -40
The output includes a feature count per layer and the layer extent. If the extent is something like (507220500.0 5414912300.0) - (507225200.0 5414917600.0), you’re staring at UTM coordinates in millimeters. If it’s (507220.5 5414912.3) - (507225.2 5414917.6), the file is in meters. Anything around 30-40,000,000 in the eastings is almost certainly survey feet over a US state plane; below 1000 and you have a model-space drawing with no real-world coordinates at all.
Inside AutoCAD, the equivalent check is UNITS (which shows the dialog) or just INSUNITS (which prints the integer enum value). For Civil 3D files, the command alias MAPSTATUSBAR shows the assigned coordinate system if one was set in Map 3D — but most plain DWGs from a structural surveyor will not have one.
Why This Matters for the GeoConvert Workflow
The DWG-to-shapefile or DWG-to-GeoPackage step is where this fails silently most often. A user runs a conversion, sees the layer count is right, sees the feature count is right, and assumes the data is correct. The geometry is in the file, but at the wrong scale. The validation you’d want — “do the eastings fall inside the assigned CRS’s normal range” — is the same kind of check that catches the field truncation and CRS mismatch issues that shapefile preflight needs and that the GeoJSON-to-shapefile silent corruption problem gets bitten by from the other direction.
GeoConvert flags the unit-mismatch case during conversion. When you load a DWG and assign a target CRS, the converter checks whether the source extent is plausible for that CRS — if the eastings are 1000x too large for UTM, it warns and offers to apply the rescale automatically. The check is simple: every projected CRS has a defined area of use, and PROJ exposes that area’s coordinate bounds. If the source extent is off by a factor that matches a common unit ratio (1000 for mm-to-m, 304.8 for mm-to-ft, 12 for in-to-ft, 39.37 for in-to-m), the converter assumes the source is in that unit and proceeds with the rescale. The user can override.
The Workflow That Stops Repeating the Problem
If your team handles a steady flow of DWGs from external surveyors, contractors, or architects, set up the receive step once and stop debugging individually:
- Maintain a list of trusted source CRS strings keyed by sender. The architect uses inches over State Plane; the structural surveyor uses millimeters over UTM; the city’s GIS team uses meters over a custom local grid.
- On receipt of a DWG, run
ogrinfo -ro -al -soand compare the extent against each trusted candidate’s range. - Convert with the matching ogr2ogr invocation. Save the GeoPackage, not the DWG, as your working format.
- Keep the DWG as an archive. If you need to send something back, do the unit math in reverse on the way out.
A common mistake at step 3 is keeping the DWG as the working file and reprojecting on display. If you do that, every downstream operation has to remember the unit story. Converting once and moving to GeoPackage means the unit is now meters by definition, the CRS is embedded, and any QGIS user opening the file sees it correctly without ceremony.
For interchange between projects, the conversation about which format to use after the import has been done well covers when to keep things in GeoPackage versus moving to GeoParquet for analytics-heavy work, and why GeoJSON should not be the working format even though it survives DWG import cleanly.
What Surveyors Should Send
If you’re on the surveying side and want to make life easier for the GIS team downstream, two things help:
- Set the drawing units to meters before placing real-world UTM or State Plane coordinates. The few title-block details that still want millimeters can scale at plot time. This puts the file in the unit that matches the projection unit and removes a whole class of import surprises.
- Include a
.prjsidecar — a text file containing the WKT of the CRS, named to match the DWG. QGIS, ArcGIS, and ogr2ogr all read this if present. It’s not officially part of the DWG spec, but it has become a de-facto convention. Tools that don’t expect it will ignore it; tools that do expect it will skip the assign-CRS step entirely.
Both changes take seconds and prevent every form of the misalignment described above.