#!/usr/bin/env python # Written and Copyright 2008 by Alan K. Edmonds (edmonds_a@msn.com) # requires Tkinter and Scientific Python # Program assumes DLG-Optional format # Documentation below on records in DLG-O format is taken from National Mapping Program, Technical Instructions, # Part2, Specifications, Standards for Digital Line Graphs revision 9/99 #The actual sequence of records in an optional distribution format DLG file is as #follows: # # 1. Header records # Ten file identification and # description records # Accuracy records (not currently used) # Control point identification records # (one per control point) # Data category identification records # (one per data category in the file) # # 2. Data records # # Node identification record # Node-to-area linkage record(s)* # Node-to-line linkage record(s) # Attribute code record(s) # Text record(s) # (Repeated for each node within a data category) # # Area identification record # Area-to-node linkage record(s)* # Area-to-line linkage record(s) # Coordinate string record(s)* # Attribute code record(s) # Text record(s) # (Repeated for each area within a data category) # # Line identification record # Coordinate string record(s) # Attribute code record(s) # Text record(s) # (Repeated for each line within a data category) # import Tk modules from Tkinter import * import tkFileDialog import tkMessageBox # import FORTRAN format modules from ScientificPython from Scientific.IO.FortranFormat import FortranFormat, FortranLine # Header read functions grouped by FORTRAN file format #FILE IDENTIFICATION AND DESCRIPTION RECORDS # #Record Data Type Starting Ending #Number Element Contents (Fortran Notation) Format Byte Byte* Comment # #1 1 Banner ALPHA A72 1 72 "***DLG-OPTIONAL FORMAT PRODUCED BY USGS PROSYS RELEASE x.x.x *** " def hdrBanner(line): format = FortranFormat('A72') fline = FortranLine(line,format) ## for j in range(0,1): ## print fline[j] return fline[0] #Record Data Type Starting Ending #Number Element Contents (Fortran Notation) Format Byte Byte* Comment #2 1 Name of digital ALPHA A40 1 40 The authorized cell name cartographic unit followed by a comma, # space, and the State two-character designator(s), separated by # hyphens. Abbreviations for other countries, such as Canada and # Mexico, shall not be represented in the DLG header. #--- --- Filler --- --- 41 41 1 space #2 2 Date of original ALPHA A10 42 51 Only one date is now source material allowed, input date in # bytes 42-45. If two dates are used, dates are separated # with a comma and space. #2 3 Collection procedure ALPHA A1 52 52 No longer required. P for qualifier photorevision, I for photoinspection, # L for limited revision, and D for digital revision were once valid codes. #2 4 Scale of original INTEGER*4 I8 53 60 Scale denominator of source source material material; for example, 24000, # 100000, or 2000000. #2 5 Sectional indicator ALPHA --- 64 66 Codes S, F, or T for (100K files) size of section, plus sequence number. #--- --- Filler --- --- 67 72 6 spaces def hdrSource(line): format = FortranFormat('A40,1X,A10,A1,I8,3X,A1') fline = FortranLine(line,format) ## for j in range(0,5): ## print fline[j] return fline[0] #Record Data Type Starting Ending #Number Element Contents (Fortran Notation) Format Byte Byte* Comment #--- --- Filler --- --- 1 41 41 spaces #3 1 Largest primary ALPHA A4 42 45 Largest primary contour contour interval interval, followed by the # interval unit (1=feet,2=meters). Present only if two or more primary # intervals exist. (selected categories) #3 2 Comma ALPHA A1 46 46 comma separator #3 3 Largest primary ALPHA A4 47 50 Largest primary bathybathymetric contour metric interval, followed # interval by the interval unit (1=feet, 2=meters, 3=fathoms). # Present only if two or more primary intervals exist.(selected categories) #--- --- Filler --- -- 51 51 1 space #3 4 Smallest primary ALPHA A4 52 55 Smallest or only primary contour interval contour interval, followed # by the interval unit as described above (selected categories). #3 5 Comma ALPHA A1 56 56 comma separator #3 6 Smallest primary ALPHA A4 57 60 Smallest or only primary bathymetric contour bathymetric contour interval, # interval followed by the interval unit as described above # (selected categories). #3 7-9 Coded Flags ALPHA A1 61 63 3 flags for future use #3 10 Coded Flag ALPHA A1 64 64 Database coded edge flag for internal NMD use. #3 11 EDGEWS ALPHA A1 65 65 Status flag for west edge, values are: b=unchecked, # 0=passed, 1=alignment discontinuity, 2=attribute discontinuity, 3=attribute # and alignment discontinuity. #3 12 EDGEWR ALPHA A1 66 66 Reason for EDGEWS, values are: b=no problem, 4=adjacent data do not exist, # 5=adjacent data unavailable, 6=temporal/source discontinuity, 7=mismatch valid, # 8=paneling unauthorized, 9=processing software limitations #3 13 EDGENS ALPHA A1 67 67 Status flag for north edge, values are b,0,1,2, or 3 as above. #3 14 EDGENR ALPHA A1 68 68 Reason for EDGENS, values are b,4,5,6,7,8, or 9 as above. #3 15 EDGEES ALPHA A1 69 69 Status flag for east edge, values are b,0,1,2, or 3 as above. #3 16 EDGEER ALPHA A1 70 70 Reason for EDGEES, values are b,4,5,6,7,8, or 9 as above. #3 17 EDGESS ALPHA A1 71 71 Status flag for south edge, values are b,0,1,2, or 3 as above. #3 18 EDGESR ALPHA A1 72 72 Reason for EDGESS, values are b,4,5,6,7,8, or 9 as above. def hdrIntervals(line): format = FortranFormat('41X,A4,A1,A4,1X,A4,A1,A4,12A1') fline = FortranLine(line,format) ## for j in range(0,18): ## print fline[j] return #Record Data Type Starting Ending #Number Element Contents (Fortran Notation) Format Byte Byte* Comment #4 1 DLG level code INTEGER*2 I6 1 6 Code=3 DLG-3, Code=2 DLG-2 #4 2 Code defining ground INTEGER*2 I6 7 12 Code=1 UTM (24K and 100K), planimetric reference, Code=3 Albers Conical Equal # system Area (2M files) #4 3 Code defining zone in INTEGER*2 I6 13 18 Code for appropriate UTM ground planimetric zone (24K or 100K files), # reference system Code=9999 for 2M files #4 4 Code defining units INTEGER*2 I6 19 24 Code=2, meters # of measure for ground planimetric coordinates # throughout the file #4 5 Resolution REAL*4 D18.11 25 42 The true ground distance corresponding to 0.001 inch at map scale. # Scale Resolutions # 1:24,000 0.61 M # 1:25,000 0.635 M # 1:48,000 1.22 M # 1:62,500 1.587 M # 1:63,360 1.61 M # 1:100,000 2.54 M # 1:250,000 6.35 M # 1:2,000,000 50.80 M #4 6 Number of file-to- INTEGER*2 I6 43 48 number=4 # map transformation # parameters #4 7 Number of accuracy/ INTEGER*2 I6 49 54 Currently=0, none included # miscellaneous records #4 8 Number (n) of INTEGER*2 I6 55 60 n=4 These points are usually, but not always, a definition # control points of the file coverage. #4 9 Number (q) of categories INTEGER*2 I6 61 66 q=1 # in the DLG file #4 10 Horizontal Datum INTEGER*2 I3 67 69 Horizontal Datum of DLG # 'b' or 0 = NAD 27 # 1 = NAD 83 # 2 = Puerto Rico # 3 = Old Hawaiian # 4 = Local (Astro) #4 11 Vertical Datum INTEGER*2 I3 70 72 Vertical Datum of DLG # 'b' or 0 = NGVD 29 # 1 = NAVD 88 # 2 = Local Mean Sea Level def hdrDLGParms(line): format = FortranFormat('4I6,D18.11,4I6,2I3') fline = FortranLine(line,format) ## for j in range(0,11): ## print fline[j] return #Record Data Type Starting Ending #Number Element Contents (Fortran Notation) Format Byte Byte* Comment #5-9 1 Projection parameters REAL*8 3D24.15 1 72 Three parameters on each of for map transformation # 5 records (see Appendix 2-4). def hdrProjParms(line): format = FortranFormat('3D24.15') fline = FortranLine(line,format) ## for j in range(0,3): ## print fline[j] return #Record Data Type Starting Ending #Number Element Contents (Fortran Notation) Format Byte Byte* Comment #10 1 Internal file-to- REAL*4 4D18.11 1 72 X, Y coordinates resulting from this transformation # map projection will be expressed in the appropriate ground planimetric # transformation coordinate system. If the x, y coordinates are already # parameters in the ground coordinate system, the projection parameters # will be: A1=1.0, A2=0.0, A3=0.0, and A4=0.0. def hdrFileProjParms(line): format = FortranFormat('4D18.11') fline = FortranLine(line,format) ## for j in range(0,4): ## print fline[j] return #Record Data Type Starting Ending #Number Element Contents (Fortran Notation) Format Byte Byte* Comment #1-n 1 Control-point label ALPHA A6 1 6 "SW," "NW," "NE," or "SE" for four quadrangle # corners. Field is padded with trailing blanks. # 2 Latitude REAL*4 F12.6 7 18 In degrees and decimal degrees. # 3 Longitude REAL*4 F12.6 19 30 In degrees and decimal degrees. # Filler 31 36 6 spaces # 4 X coordinate REAL*4 F12.2 37 48 In units in the appropriate zone of the ground planimetric # coordinate system. # 5 Y coordinate REAL*4 F12.2 49 60 In units in the appropriate zone of the ground planimetric # coordinate system. #--- --- Filler --- --- 61 72 12 spaces def hdrCtrlPts(line): format = FortranFormat('A6,2F12.6,6X,2F12.2') fline = FortranLine(line,format) ## for j in range(0,5): ## print fline[j] return fline[0], fline[3], fline[4] #Record Data Type Starting Ending #Number Element Contents (Fortran Notation) Format Byte Byte* Comment #1-q 1 Category name ALPHA A20 1 20 The first 4 characters are unique to USGS/NMD data. # 2 Attribute format codes INTEGER*2 I4 21 24 Blank or zero (0) indicates default (2I6) attribute # formatting in major-minor pairs. # 3 Highest node INTEGER*2 I6 25 30 Number of nodes referenced identification number. in the file. # 4 Actual number of INTEGER*2 I6 31 36 Only if the DCF is not packed, and the element # nodes in file ID numbers not compressed, will this number be different # from data element 3. # --- Filler --- --- 37 37 1 space # 5 Presence of node-to- INTEGER*2 I1 38 38 0=node-area list not included, 1=node-area list included. # area linkage records # 6 Presence of node-to- INTEGER*2 I1 39 39 0=node-line list not included, 1=node-line list included. # line linkage records # --- Filler --- --- 40 40 1 zero or space # 7 Highest area ID number INTEGER*2 I6 41 46 Number of areas referenced in the file. # 8 Actual number of areas INTEGER*2 I6 47 52 Only if the DCF is not packed, and the element ID # in file numbers not compressed, will this number be different # from data element 7. # --- Filler --- --- 53 53 1 space # 9 Presence of area-to- INTEGER*2 I1 54 54 0=area-node list not included, 1=area-node list included. # node linkage records # 10 Presence of area-to- INTEGER*2 I1 55 55 0=area-line list notincluded, 1=area-line list included. # line linkage records # 11 Presence of area- INTEGER*2 I1 56 56 0=area coordinates not included, 1=area coordinates included. # coordinate lists # 12 Highest line INTEGER*2 I6 57 62 Number of lines identification number referenced in the file # 13 Actual number of INTEGER*2 I6 63 68 Only if the DCF is not packed, and the element ID numbers not # lines in file compressed, will this number be different from data element 12. # --- Filler --- --- 69 71 3 spaces # 14 Presence of line- INTEGER*2 I1 72 72 0=line coordinates not included, 1=line coordinate list included. # coordinate lists def hdrCategory(line): format = FortranFormat('A20,I4,2I6,1X,2I1,1X,2I6,1X,3I1,2I6,3X,I1') fline = FortranLine(line,format) for j in range(0,14): print fline[j] if j == 0: CATEGORY = fline[j] if j == 3: NODES = fline[j] if j == 7: AREAS = fline[j] if j == 12: LINES = fline[j] return CATEGORY, NODES, AREAS, LINES # NODE AND AREA IDENTIFICATION RECORDS #Record Data Type Starting Ending #Number Element Contents (Fortran Notation) Format Byte Byte* Comment # 1 Record type ALPHA A1 1 1 "N" or "A" # 2 Element internal INTEGER*2 I5 2 6 Number is positive and seqID number uential from 1-n within each # element type, where n is the highest element ID number. # 3 Coordinates of node REAL*4 2F12.2 7 30 The area point is usually,but not always within the # point or representative point for area polygon it represents. # 4 Number of elements in INTEGER*2 I6 31 36 # an area list (for nodes), or a node list (for areas) # 5 Number of elements in INTEGER*2 I6 37 42 Number of line segments that intersect at the node or, for # line list areas, line segments plus number of islands. # 6 Number of x,y or INTEGER*2 I6 43 48 For area records only, blank for node records. # lat-long points in area-coordinate list # 7 Number of attribute INTEGER*2 I6 49 54 # code pairs listed # 8 Number of text INTEGER*2 I6 55 60 Zero (0). There are no listed text attributes for DLG data. # characters # 9 Number of islands INTEGER*2 I6 61 66 For area records only, blank within area for node records. # --- Filler --- --- 67 72 6 spaces def NodeAreaID(line): format = FortranFormat('A1,I5,2F12.2,6I6,6X') fline = FortranLine(line,format) #for j in range(0,10): # if j == 0: print 'Record Type = ', fline[j] # if j == 1: print 'Number = ', fline[j] # if j == 4: NumAreasNodes = fline[j] # if j == 5: NumLines = fline[j] # if j == 6: NumAreaCoords = fline[j] #print fline[4], fline[5], fline[6], fline[7] return fline[4], fline[5], fline[6], fline[7] # LINE IDENTIFICATION RECORDS #Record Data Type Starting Ending #Number Element Contents (Fortran Notation) Format Byte Byte* Comment # 1 Record type A1 1 1 "L" # 2 Element internal ID number I5 2 6 Number is positive and sequential from 1-n within each # element type, where n is the highest element ID number. # 3 Starting node I6 7 12 Internal ID number. Refers to data element 2 of the # node identification record. # 4 Ending node I6 13 18 Internal ID number. Refers to data element 2 of the # node identification record. # 5 Left area I6 19 24 Internal ID number. Refers to data element 2 of the # area identification record. # 6 Right area I6 25 30 Internal ID number. Refers to data element 2 of the # area identification record. # --- Filler --- 31 42 12 spaces # 7 Number of x,y coordinate I6 43 48 Number of coordinate pairs listed. # pairs listed # 8 Number of attribute I6 49 54 # code pairs listed # 9 Number of text characters listed I6 55 60 Zero (0). There are no text attributes for DLG data. def LineID(line): format = FortranFormat('A1,I5,4I6,12X,3I6') fline = FortranLine(line,format) #for j in range(0,10): # if j == 0: print 'Record Type = ', fline[j] # if j == 1: print 'Number = ', fline[j] # if j == 4: NumAreasNodes = fline[j] # if j == 5: NumLines = fline[j] # if j == 6: NumAreaCoords = fline[j] #print fline[6], fline[7] return fline[6], fline[7] def LineCoords(line): format = FortranFormat('(3(2F12.2))') fline = FortranLine(line,format) return fline # Open DLG file for read readfile = tkFileDialog.askopenfilename(filetypes = [('DLG Files', '*.dlg'), ('All Files', '*')],title='Select Input File') # If file name selected, read in data, else quit if readfile: print readfile file = open(readfile,'r') print file savefile = readfile[:-4] + '.dxf' print savefile # Read DLG Header for i in range(1,16): print 'Header Line %2d'% i line = file.readline() if i == 1: hdrBanner(line) if i == 2: result = hdrSource(line) identity = result if i == 3: hdrIntervals(line) if i == 4: hdrDLGParms(line) if i == 5: hdrProjParms(line) if i == 6: hdrProjParms(line) if i == 7: hdrProjParms(line) if i == 8: hdrProjParms(line) if i == 9: hdrProjParms(line) if i == 10: hdrFileProjParms(line) if i == 11: ctrlpts = hdrCtrlPts(line) print ctrlpts dir = ctrlpts[0][0:2] if dir == 'SW': xmin = ctrlpts[1] ymin = ctrlpts[2] if dir == 'NE': xmax = ctrlpts[1] ymax = ctrlpts[2] if i == 12: ctrlpts = hdrCtrlPts(line) print ctrlpts dir = ctrlpts[0][0:2] if dir == 'SW': xmin = ctrlpts[1] ymin = ctrlpts[2] if dir == 'NE': xmax = ctrlpts[1] ymax = ctrlpts[2] if i == 13: ctrlpts = hdrCtrlPts(line) print ctrlpts dir = ctrlpts[0][0:2] if dir == 'SW': xmin = ctrlpts[1] ymin = ctrlpts[2] if dir == 'NE': xmax = ctrlpts[1] ymax = ctrlpts[2] if i == 14: ctrlpts = hdrCtrlPts(line) print ctrlpts dir = ctrlpts[0][0:2] if dir == 'SW': xmin = ctrlpts[1] ymin = ctrlpts[2] if dir == 'NE': xmax = ctrlpts[1] ymax = ctrlpts[2] if i == 15: result = hdrCategory(line) category = result[0] identity = identity + ' - ' + category print 'Nodes = ', result[1], 'Areas = ', result[2], 'Lines = ', result[3] print 'Identity = ', identity print 'EXTENTS = ', xmin, ymin, xmax, ymax print 'END OF HEADER' # Read data # Confirm input file, else quit if tkMessageBox.askokcancel("Convert this file?",identity): # Ask for file save name, default is input filename with dxf extenstion, else quit save = tkFileDialog.asksaveasfile(filetypes = [('DXF Files', '*.dxf'), ('All Files', '*')], initialfile=savefile, mode='w',title='Select Output File') if save: # define dxf strings zero = ' 0\n' two = ' 2\n' five = ' 5\n' eight = ' 8\n' nine = ' 9\n' ten = ' 10\n' twenty = ' 20\n' seventy= ' 70\n' ninety = ' 90\n' hundred = '100\n' section = 'SECTION\n' endsec = 'ENDSEC\n' dheader = 'HEADER\n' entities = 'ENTITIES\n' extmax = '$EXTMAX\n' extmin = '$EXTMIN\n' lwpolyline = 'LWPOLYLINE\n' acdbentity = 'AcDbEntity\n' acdbpolyline = 'AcDbPolyline\n' vertex = 'VERTEX\n' neof = 'EOF\n' # write out dxf header save.write(zero) save.write(section) save.write(two) save.write(dheader) save.write(nine) save.write(extmin) save.write(ten) save.write(str(xmin)+'\n') save.write(twenty) save.write(str(ymin)+'\n') save.write(nine) save.write(extmax) save.write(ten) save.write(str(xmax)+'\n') save.write(twenty) save.write(str(ymax)+'\n') save.write(zero) save.write(endsec) # start writing DXF entity section save.write(zero) save.write(section) save.write(two) save.write(entities) print 'Starting Nodes' # read through and skip nodes for i in range(0,result[1]): line = file.readline() ids = NodeAreaID(line) for k in range(0,ids[1],12): line = file.readline() for k in range(0,ids[3],12): line = file.readline() print 'Starting Areas' # read through and skip areas for i in range(0,result[2]): line = file.readline() ids = NodeAreaID(line) for k in range(0,ids[1],12): line = file.readline() for k in range(0,ids[3],12): line = file.readline() print 'Starting Lines' # read through and write out lines seqnum = 0 for i in range(0,result[3]): line = file.readline() ids = LineID(line) # write PolyLine Header save.write(zero) save.write(lwpolyline) save.write(five) save.write(str(seqnum)+'\n') save.write(hundred) save.write(acdbentity) save.write(eight) save.write(category+'\n') save.write(hundred) save.write(acdbpolyline) save.write(ninety) save.write(str(ids[0])+'\n') save.write(seventy) save.write(zero) # write out line coordinates for k in range(0,ids[0],3): # read number of coordinates line = file.readline() coords = LineCoords(line) for l in range(0,5,2): if coords[l] != 0.0: # write each x (10) and y (20) coordinate save.write(ten) save.write(str(coords[l])+'\n') save.write(twenty) save.write(str(coords[l+1])+'\n') for k in range(0,ids[1],6): line = file.readline() seqnum = seqnum + 1 # end section save.write(zero) save.write(endsec) # write out EOF for dxf file & close output file save.write(zero) save.write(neof) save.close() # close input file else: file.close()