Jump to content

Export layers with objectdata to GIS format


Recommended Posts

Posted

Hi all,

 

I get a DWG with multiple layers with lines and such.

Now... each line has Object Data linked to it.

 

I need to make a export to GIS, where each layer is its own layer in QGIS.

 

When i make a export to SHP or GML, and load it in QGIS, all layer informatie is gone and is put on only GIS layer.

 

What is the most effective way to export AutoCAD layers to GIS layers?

Posted

I just use the QGIS Import for drawings from AutoCAD and I get the layers.

 

I think for surfaces, QGIS needs DEM, IIRC _SurfaceExportToDem is in Civil 3D.

 

Maybe in QGIS Import the SHP or just the .dwg and Go to "Layer" then  "Add Layer" then "Add Vector Layer".

 

My QGIS is at home, so I think those steps are close.

Posted

I forgot all about this over the weekend, can you post a sample drawing?

 

I'll try to find time this weekend to run through this in QGIS at home.

Posted

@SLW210 here is a sample DWG

Sample.dwg

 

There are 4 'Object Data' tables in this DWG.

I'd like to see each 'AutoCAD layer' on its own 'QGIS' layer, including only the assigned Object DATA'.

 

MAPEXPORT only seems to allow to export all object data for some reason.

  • 2 weeks later...
Posted

How did you go about creating the SHP?

 

I don't have MAP at home so I'll have to try some things when I get back to work and double check.

 

But, looking through the QGIS information, that should be the way to go.

 

Exporting object classes to ESRI Shape file from AutoCAD® Map 3D

 

In QGIS same as mentioned before, go to the Layer menu>Select Add Layer > Add Vector Layer.

In the dialog, choose File and browse to your shapefile (.shp) location>Click Open to load the shapefile into QGIS.

 

That should be the correct way according to the documentation.

 

Also, I believe QGIS needs AutoCAD 2018 or older files, may be the same with the SHPs, your profile shows AutoCAD 2018, so is that the case here for the Map 3D?

Posted

@SLW210

 

This weekend i finished a LISP that does exactly what i need, since there doesnt seem te be a regular AutoCAD-Map3D function for this.

 

This code collects all object data on a layer, creates a 'exportprofile' for this specific layer and export this to its own SHP File.

Since all EPF files are created for an individual layer, each SHP only gets the assigned ObjectData instead of 'All' objectdata in the DWG.

 

code is based on the Dutch NLCS Cad standard for layer detection versus points/lines/polygons.

 

;;; ------------------------------------------------------------
;;; MAPEXP_OD_ALL.LSP
;;; Export each layer to its own SHP using -MAPEXPORT + per-layer EPF
;;; - One SHP per layer in a subfolder:
;;;     <DWGNAME>_YYYYMMDD_HHMMSS under the DWG folder
;;; - Uses DWGTITLED: if DWG not saved, alert & abort
;;; - Geometry type controlled by layer name suffix:
;;;     * -S  -> Point
;;;     * -G  -> Line
;;;     * -GV -> Polygon
;;;   Layers without those suffixes fall back to first-entity detection.
;;; - Per-layer Object Data (OD) mapped via ExpressionFieldMappings
;;; - EPF filters by that layer (DoFilterByLayer=1 + LayerList)
;;; - Treats closed polylines as polygons
;;; - Temp EPF is deleted after export
;;; - If SHP already exists: Overwrite via default ENTER, Load Profile? = Y
;;; - SHP filename: all '.' in the *layer name* are changed to ',' in the output file
;;; ------------------------------------------------------------

(vl-load-com)

;;; Global export folder (set in c:MAPEXP_OD_ALL)
(setq *mapexp-export-folder* nil)

;;; --- small helpers ---

(defun mapexp-get-dwg-folder ( / p )
  ;; Use DWGPREFIX; ensure it ends with a backslash
  (setq p (getvar "DWGPREFIX"))
  (if (and p (/= p "") (/= (substr p (strlen p) 1) "\\"))
    (setq p (strcat p "\\"))
  )
  p
)

(defun mapexp-pad2 (n)
  ;; Pad single digit to 2 chars (e.g. 7 -> "07")
  (if (< n 10) (strcat "0" (itoa n)) (itoa n))
)

(defun mapexp-get-datetime ( / s lst )
  ;; Get current date/time using EDTIME (compatible across versions)
  ;; Returns list: (year month day hour minute second)
  ;; %Y = year, %m = month, %d = day, %H = hour, %M = minute, %S = second
  ;; EDTIME format here: "YYYY MM DD HH MM SS"
  (setq s (menucmd "M=$(edtime,$(getvar,DATE),YYYY MM DD HH MM SS)"))
  ;; s is like "2025 11 21 14 32 05"
  ;; Turn it into "(2025 11 21 14 32 05)" and READ it
  (setq lst (read (strcat "(" s ")")))
  lst
)

(defun mapexp-get-export-folder ( / base path dt year mon day hh mm ss folder )
  ;; Create export folder:
  ;;   <DWGNAME_without_ext>_YYYYMMDD_HHMMSS
  ;; in the same folder as the DWG
  (setq path (getvar "DWGPREFIX"))
  (setq base (vl-filename-base (getvar "DWGNAME")))

  ;; Use EDTIME-based datetime function
  (setq dt   (mapexp-get-datetime))
  (setq year (itoa (nth 0 dt)))
  (setq mon  (mapexp-pad2 (nth 1 dt)))
  (setq day  (mapexp-pad2 (nth 2 dt)))
  (setq hh   (mapexp-pad2 (nth 3 dt)))
  (setq mm   (mapexp-pad2 (nth 4 dt)))
  (setq ss   (mapexp-pad2 (nth 5 dt)))

  (setq folder
    (strcat path "SHP_OD_EXPORT_" base "_" year mon day "_" hh mm ss "\\")
  )

  ;; Create folder if it doesn't exist yet
  (if (not (vl-file-directory-p folder))
    (vl-mkdir folder)
  )

  folder
)

(defun mapexp-sanitize-filename ( name / bad i ch )
  ;; Replace characters that are invalid in file names
  (setq bad (list 34 42 47 58 60 62 63 92 124)) ; " * / : < > ? \ |
  (setq i 0)
  (while (< i (strlen name))
    (setq ch (ascii (substr name (1+ i) 1)))
    (if (member ch bad)
      (setq name
            (strcat (substr name 1 i)
                    "_"
                    (substr name (+ i 2))
            )
      )
      (setq i (1+ i))
    )
  )
  name
)

;;; For the OUTPUT FILE NAME ONLY:
;;; - change '.' to ',' in the layer name
;;; - then sanitize for filesystem (quotes, *, /, :, <, >, ?, \, |)
(defun mapexp-make-output-name (lay / s)
  (setq s lay)
  ;; Replace all dots with commas
  (setq s (vl-string-subst "," "." s))
  ;; Remove OS-invalid characters but keep spaces, dashes, commas, etc.
  (setq s (mapexp-sanitize-filename s))
  s
)

(defun mapexp-first-entity-on-layer ( lay / ss ent )
  (setq ss (ssget "X" (list (cons 8 lay))))
  (if (and ss (> (sslength ss) 0))
    (ssname ss 0)
    nil
  )
)

;;; Fallback geometry detection – if no suffix rule hit
(defun mapexp-geometry-type-from-entity ( ent / ed typ flags )
  ;; Returns one of "Point" "Line" "Polygon" or nil
  (setq ed  (entget ent))
  (setq typ (cdr (assoc 0 ed)))
  (cond
    ((member typ '("POINT" "MULTILEADER" "INSERT")) "Point")
    ((member typ '("LINE" "ARC" "CIRCLE")) "Line")
    ((member typ '("LWPOLYLINE" "POLYLINE"))
     (setq flags (cdr (assoc 70 ed)))
     (if (and flags (= (logand flags 1) 1))
       "Polygon"
       "Line"
     )
    )
    ((member typ '("SPLINE"))             "Line")
    ((member typ '("HATCH" "POLYGON"))    "Polygon")
    (T nil)
  )
)

;;; Geometry type forced by layer name suffix
(defun mapexp-geomtype-from-layername ( lay / )
  ;; rules:
  ;;   * -GV -> Polygon
  ;;   * -S  -> Point
  ;;   * -G  -> Line
  (cond
    ((wcmatch lay "*-GV") "Polygon")
    ((wcmatch lay "*-S")  "Point")
    ((wcmatch lay "*-G")  "Line")
    (T nil)
  )
)

;;; Sanitize a string to be a valid FDO property name:
;;; - Only A–Z, a–z, 0–9, _
;;; - If first char is not a letter or _, prefix with "F_"
(defun mapexp-sanitize-fdo-name (s / i ch result)
  (if (not s) (setq s "FDO_NAME"))
  (setq result "")
  (setq i 1)
  (while (<= i (strlen s))
    (setq ch (substr s i 1))
    (if (wcmatch ch "[A-Za-z0-9_]")
      (setq result (strcat result ch))
      (setq result (strcat result "_"))
    )
    (setq i (1+ i))
  )
  ;; make sure first char is letter or _
  (if (or (= result "")
          (not (wcmatch (substr result 1 1) "[A-Za-z_]"))
      )
    (setq result (strcat "F_" result))
  )
  result
)

;;; Get unique OD table names used on a given layer
(defun mapexp-get-od-tables-on-layer (layname / ss i e odtabs tbls tabName)
  (setq tbls '())
  (setq ss (ssget "X" (list (cons 8 layname)))) ; all ents on layer
  (if ss
    (progn
      (setq i 0)
      (while (< i (sslength ss))
        (setq e      (ssname ss i)
              odtabs (ade_odgettables e) ; Map 3D ADE function
        )
        (foreach tabName odtabs
          (if (and tabName (not (member tabName tbls)))
            (setq tbls (cons tabName tbls))
          )
        )
        (setq i (1+ i))
      )
    )
  )
  (reverse tbls)
)

;;; --- EPF writer: SHP + OD + layer filter ---

(defun mapexp-write-epf-with-od
       ( epfpath geomType layName odTabs
         / file tabName def_tbl cols col colName colType
           dataType usedNames outName baseName idx )

  ;; geomType must be "Point" "Line" or "Polygon"
  (setq file (open epfpath "W"))
  (if (null file)
    nil
    (progn
      ;; Header – based on working AdMapExportProfile structure for SHP

      (princ "<AdMapExportProfile version=\"2.1.3\">" file)
      (princ "<LoadedProfileName/>" file)

      (princ "<StorageOptions>" file)
      (princ "<StorageType>FileOneEntityType</StorageType>" file)
      (princ "<GeometryType>" file)
      (princ geomType file)
      (princ "</GeometryType><FilePrefix/></StorageOptions>" file)

      ;; Auto-selection; filter by layer below
      (princ "<SelectionOptions><UseSelectionSet>0</UseSelectionSet><UseAutoSelection>1</UseAutoSelection></SelectionOptions>" file)

      (princ "<TranslationOptions>" file)
      ;; treat closed polylines as polygons
      (princ "<TreatClosedPolylinesAsPolygons>1</TreatClosedPolylinesAsPolygons>" file)
      (princ "<ExplodeBlocks>1</ExplodeBlocks>" file)
      (princ "<LayersToLevels><MapLayersToLevels>0</MapLayersToLevels><LayerToLevelMapping/></LayersToLevels>" file)
      (princ "</TranslationOptions>" file)

      (princ "<TopologyOptions><GroupComplexPolygons>0</GroupComplexPolygons><TopologyName/></TopologyOptions>" file)

      ;; Filter by this layer only
      (princ "<LayerOptions>" file)
      (princ "<DoFilterByLayer>1</DoFilterByLayer>" file)
      (princ "<LayerList>" file)
      (princ layName file)
      (princ "</LayerList>" file)
      (princ "</LayerOptions>" file)

      (princ "<FeatureClassOptions><DoFilterByFeatureClass>0</DoFilterByFeatureClass><FeatureClassList/></FeatureClassOptions>" file)

      ;; TableDataType "None" – OD via ExpressionFieldMappings
      (princ "<TableDataOptions>" file)
      (princ "<TableDataType>None</TableDataType>" file)
      (princ "<Name/>" file)
      (princ "<SQLKeyOnly>0</SQLKeyOnly>" file)
      (princ "</TableDataOptions>" file)

      (princ "<CoordSysOptions><DoCoordinateConversion>0</DoCoordinateConversion><CoordSysName/></CoordSysOptions>" file)

      ;; SHP target
      (princ "<TargetNameOptions><FormatName>SHP</FormatName></TargetNameOptions>" file)

      (princ "<DriverOptions/>" file)
      (princ "<UseUniqueKeyField>0</UseUniqueKeyField><UseUniqueKeyFieldName>AdMapKey</UseUniqueKeyFieldName>" file)

      ;; ===== OD ExpressionFieldMappings =====
      (princ "<ExpressionFieldMappings>" file)

      (setq usedNames '()) ; track used attribute names to avoid duplicates

      (foreach tabName odTabs
        (setq def_tbl (ade_odtabledefn tabName))   ;; ADE table definition
        ;; def_tbl: (("TableName" . "...") ("Description" . "...") ("Columns" . ( ... )))
        (setq cols (cdr (assoc "Columns" def_tbl)))

        (foreach col cols
          (setq colName (cdr (assoc "ColName" col)))
          (setq colType (cdr (assoc "ColType" col)))
          (if colName
            (progn
              ;; Map OD type to EPF Datatype
              (setq dataType
                (cond
                  ((and colType (wcmatch (strcase colType) "*INT*"))
                   "IntegerDataType"
                  )
                  ((and colType (wcmatch (strcase colType) "*REAL*,*DOUBLE*,*FLOAT*,*NUM*"))
                   "DoubleDataType"
                  )
                  (T "CharacterDataType")
                )
              )

              ;; Decide attribute (FDO property) name:
              ;; - sanitize to valid FDO name
              ;; - if duplicate, append 2,3,...
              (setq baseName (mapexp-sanitize-fdo-name colName))
              (setq outName  baseName
                    idx      1
              )
              (while (member (strcase outName) usedNames)
                (setq idx (1+ idx))
                (setq outName (strcat baseName (itoa idx)))
              )
              (setq usedNames (cons (strcase outName) usedNames))

              ;; <NameValuePair> mapping:
              ;; <Name>outName</Name>       -> FDO-safe property name
              ;; <Value>:ColName@Table</Value> -> actual OD mapping
              ;; <Datatype>...</Datatype>
              (princ "<NameValuePair><Name>" file)
              (princ outName file)
              (princ "</Name><Value>:" file)
              (princ colName file)
              (princ "@" file)
              (princ tabName file)
              (princ "</Value><Datatype>" file)
              (princ dataType file)
              (princ "</Datatype></NameValuePair>" file)
            )
          )
        )
      )

      (princ "</ExpressionFieldMappings>" file)
      (princ "</AdMapExportProfile>" file)

      (close file)
      T
    )
  )
)

;;; --- main export per layer ---

(defun mapexp-export-layer-to-shp
       ( lay / ent geomType dwgFolder shpName shpFull epfFull odTabs ok )

  ;; Prefer explicit geometry from layer name; fall back to entity if not matched
  (setq geomType (mapexp-geomtype-from-layername lay))

  (if (null geomType)
    (progn
      (setq ent (mapexp-first-entity-on-layer lay))
      (if (null ent)
        (setq geomType nil)
        (setq geomType (mapexp-geometry-type-from-entity ent))
      )
    )
  )

  (if (null geomType)
    nil
    (progn
      ;; Use the global export folder instead of the DWG folder
      (setq dwgFolder *mapexp-export-folder*)

      ;; SHP base name: layer name, but '.' -> ',' and OS-invalid chars cleaned
      (setq shpName   (mapexp-make-output-name lay))
      (setq shpFull   (strcat dwgFolder shpName ".shp"))
      (setq epfFull   (strcat dwgFolder shpName "_temp_export.epf"))

      ;; OD tables used on this layer
      (setq odTabs (mapexp-get-od-tables-on-layer lay))

      ;; EPF for this geometry type + layer OD + layer filter
      (setq ok (mapexp-write-epf-with-od epfFull geomType lay odTabs))

      (if ok
        (progn
          ;; If SHP already exists, we expect Overwrite prompt:
          ;;   This file already exists. Enter an option [Overwrite/Cancel] <Overwrite>:
          ;; -> we send "" (ENTER) to accept default Overwrite
          ;; Then: Load Profile? [Yes/No] <No>: -> we send "Y"
          (princ "\n")
          (if (findfile shpFull)
            (command
              "-MAPEXPORT"
              "SHP"
              shpFull
              ""         ; Overwrite? -> ENTER = default Overwrite
              "Y"        ; Load Profile? Yes
              epfFull
              "Proceed"
            )
            (command
              "-MAPEXPORT"
              "SHP"
              shpFull
              "Y"        ; Load Profile? Yes (no overwrite prompt)
              epfFull
              "Proceed"
            )
          )

          ;; Delete temp EPF
          (if (findfile epfFull)
            (vl-file-delete epfFull)
          )
          T
        )
        nil
      )
    )
  )
)

;;; --- public command ---

(defun c:MAPEXP_OD_ALL ( / lay rec )

  (vl-load-com)

  ;; Check if drawing is saved
  (if (= (getvar "DWGTITLED") 0)
    (progn
      (alert
        "Deze tekening is nog niet opgeslagen.\n\nSla de DWG eerst op en start MAPEXP_OD_ALL daarna opnieuw."
      )
      (princ)
    )
    (progn
      (setvar "CMDECHO" 0)

      ;; Create export folder for this run
      (setq *mapexp-export-folder* (mapexp-get-export-folder))

      (prompt
        (strcat
          "\nMAPEXP_OD_ALL – exportfolder: "
          *mapexp-export-folder*
        )
      )

      ;; loop through all layers in the table
      (setq rec (tblnext "LAYER" T))
      (while rec
        (setq lay (cdr (assoc 2 rec)))   ; layer name
        ;; skip xref layers (contain "|")
        (if (not (wcmatch lay "*|*"))
          (mapexp-export-layer-to-shp lay)
        )
        (setq rec (tblnext "LAYER"))
      )

      (setvar "CMDECHO" 1)

      (prompt "\nMAPEXP_OD_ALL – done.")
      (princ)
    )
  )
)

 

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Unfortunately, your content contains terms that we do not allow. Please edit your content to remove the highlighted words below.
Reply to this topic...

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...