v.unpack.py 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242
  1. #!/usr/bin/env python3
  2. # -*- coding: utf-8 -*-
  3. ############################################################################
  4. #
  5. # MODULE: v.unpack
  6. # AUTHOR(S): Luca Delucchi
  7. #
  8. # PURPOSE: Unpack up a vector map packed with v.pack
  9. # COPYRIGHT: (C) 2010-2017 by the GRASS Development Team
  10. #
  11. # This program is free software under the GNU General
  12. # Public License (>=v2). Read the file COPYING that
  13. # comes with GRASS for details.
  14. #
  15. #############################################################################
  16. #%module
  17. #% description: Imports a GRASS GIS specific vector archive file (packed with v.pack) as a vector map
  18. #% keyword: vector
  19. #% keyword: import
  20. #% keyword: copying
  21. #%end
  22. #%option G_OPT_F_BIN_INPUT
  23. #% description: Name of input pack file
  24. #% key_desc: name.pack
  25. #%end
  26. #%option G_OPT_V_OUTPUT
  27. #% label: Name for output vector map
  28. #% description: Default: taken from input file internals
  29. #% required : no
  30. #% guisection: Output settings
  31. #%end
  32. #%flag
  33. #% key: o
  34. #% label: Override projection check (use current location's projection)
  35. #% description: Assume that the dataset has same projection as the current location
  36. #% guisection: Output settings
  37. #%end
  38. #%flag
  39. #% key: p
  40. #% label: Print projection information of input pack file and exit
  41. #% guisection: Print
  42. #%end
  43. import os
  44. import sys
  45. import shutil
  46. import tarfile
  47. import atexit
  48. from grass.script.utils import diff_files, try_rmdir
  49. from grass.script import core as grass
  50. from grass.script import db as grassdb
  51. from grass.exceptions import CalledModuleError
  52. def cleanup():
  53. try_rmdir(tmp_dir)
  54. def main():
  55. infile = options['input']
  56. # create temporary directory
  57. global tmp_dir
  58. tmp_dir = grass.tempdir()
  59. grass.debug('tmp_dir = %s' % tmp_dir)
  60. # check if the input file exists
  61. if not os.path.exists(infile):
  62. grass.fatal(_("File <%s> not found") % infile)
  63. # copy the files to tmp dir
  64. input_base = os.path.basename(infile)
  65. shutil.copyfile(infile, os.path.join(tmp_dir, input_base))
  66. os.chdir(tmp_dir)
  67. tar = tarfile.TarFile.open(name=input_base, mode='r')
  68. try:
  69. data_name = tar.getnames()[0]
  70. except:
  71. grass.fatal(_("Pack file unreadable"))
  72. if flags['p']:
  73. # print proj info and exit
  74. try:
  75. for fname in ['PROJ_INFO', 'PROJ_UNITS']:
  76. f = tar.extractfile(fname)
  77. sys.stdout.write(f.read().decode())
  78. except KeyError:
  79. grass.fatal(_("Pack file unreadable: file '{}' missing".format(fname)))
  80. tar.close()
  81. return 0
  82. # set the output name
  83. if options['output']:
  84. map_name = options['output']
  85. else:
  86. map_name = data_name
  87. # grass env
  88. gisenv = grass.gisenv()
  89. mset_dir = os.path.join(gisenv['GISDBASE'],
  90. gisenv['LOCATION_NAME'],
  91. gisenv['MAPSET'])
  92. new_dir = os.path.join(mset_dir, 'vector', map_name)
  93. gfile = grass.find_file(name=map_name, element='vector', mapset='.')
  94. overwrite = os.getenv('GRASS_OVERWRITE')
  95. if gfile['file'] and overwrite != '1':
  96. grass.fatal(_("Vector map <%s> already exists") % map_name)
  97. elif overwrite == '1' and gfile['file']:
  98. grass.warning(_("Vector map <%s> already exists and will be overwritten") % map_name)
  99. grass.run_command('g.remove', flags='f', quiet=True, type='vector',
  100. name=map_name)
  101. shutil.rmtree(new_dir, True)
  102. # extract data
  103. tar.extractall()
  104. tar.close()
  105. if os.path.exists(os.path.join(data_name, 'coor')):
  106. pass
  107. elif os.path.exists(os.path.join(data_name, 'cell')):
  108. grass.fatal(_("This GRASS GIS pack file contains raster data. Use "
  109. "r.unpack to unpack <%s>" % map_name))
  110. else:
  111. grass.fatal(_("Pack file unreadable"))
  112. # check projection compatibility in a rather crappy way
  113. loc_proj = os.path.join(mset_dir, '..', 'PERMANENT', 'PROJ_INFO')
  114. loc_proj_units = os.path.join(mset_dir, '..', 'PERMANENT', 'PROJ_UNITS')
  115. skip_projection_check = False
  116. if not os.path.exists(os.path.join(tmp_dir, 'PROJ_INFO')):
  117. if os.path.exists(loc_proj):
  118. grass.fatal(
  119. _("PROJ_INFO file is missing, unpack vector map in XY (unprojected) location."))
  120. skip_projection_check = True # XY location
  121. if not skip_projection_check:
  122. diff_result_1 = diff_result_2 = None
  123. if not grass.compare_key_value_text_files(filename_a=os.path.join(tmp_dir, 'PROJ_INFO'),
  124. filename_b=loc_proj, proj=True):
  125. diff_result_1 = diff_files(os.path.join(tmp_dir, 'PROJ_INFO'),
  126. loc_proj)
  127. if not grass.compare_key_value_text_files(filename_a=os.path.join(tmp_dir, 'PROJ_UNITS'),
  128. filename_b=loc_proj_units,
  129. units=True):
  130. diff_result_2 = diff_files(os.path.join(tmp_dir, 'PROJ_UNITS'),
  131. loc_proj_units)
  132. if diff_result_1 or diff_result_2:
  133. if flags['o']:
  134. grass.warning(_("Projection information does not match. Proceeding..."))
  135. else:
  136. if diff_result_1:
  137. grass.warning(_("Difference between PROJ_INFO file of packed map "
  138. "and of current location:\n{diff}").format(diff=''.join(diff_result_1)))
  139. if diff_result_2:
  140. grass.warning(_("Difference between PROJ_UNITS file of packed map "
  141. "and of current location:\n{diff}").format(diff=''.join(diff_result_2)))
  142. grass.fatal(_("Projection of dataset does not appear to match current location."
  143. " In case of no significant differences in the projection definitions,"
  144. " use the -o flag to ignore them and use"
  145. " current location definition."))
  146. # new db
  147. fromdb = os.path.join(tmp_dir, 'db.sqlite')
  148. # copy file
  149. shutil.copytree(data_name, new_dir)
  150. # exist fromdb
  151. if os.path.exists(fromdb):
  152. # the db connection in the output mapset
  153. dbconn = grassdb.db_connection(force=True)
  154. todb = dbconn['database']
  155. # return all tables
  156. list_fromtable = grass.read_command('db.tables', driver='sqlite',
  157. database=fromdb).splitlines()
  158. # return the list of old connection for extract layer number and key
  159. dbln = open(os.path.join(new_dir, 'dbln'), 'r')
  160. dbnlist = dbln.readlines()
  161. dbln.close()
  162. # check if dbf or sqlite directory exists
  163. if dbconn['driver'] == 'dbf' and not os.path.exists(os.path.join(mset_dir, 'dbf')):
  164. os.mkdir(os.path.join(mset_dir, 'dbf'))
  165. elif dbconn['driver'] == 'sqlite' and not os.path.exists(os.path.join(mset_dir, 'sqlite')):
  166. os.mkdir(os.path.join(mset_dir, 'sqlite'))
  167. # for each old connection
  168. for t in dbnlist:
  169. # it split the line of each connection, to found layer number and key
  170. if len(t.split('|')) != 1:
  171. values = t.split('|')
  172. else:
  173. values = t.split(' ')
  174. from_table = values[1]
  175. layer = values[0].split('/')[0]
  176. # we need to take care about the table name in case of several layer
  177. if options["output"]:
  178. if len(dbnlist) > 1:
  179. to_table = "%s_%s" % (map_name, layer)
  180. else:
  181. to_table = map_name
  182. else:
  183. to_table = from_table
  184. grass.verbose(_("Coping table <%s> as table <%s>") % (from_table,
  185. to_table))
  186. # copy the table in the default database
  187. try:
  188. grass.run_command('db.copy', to_driver=dbconn['driver'],
  189. to_database=todb, to_table=to_table,
  190. from_driver='sqlite',
  191. from_database=fromdb,
  192. from_table=from_table)
  193. except CalledModuleError:
  194. grass.fatal(_("Unable to copy table <%s> as table <%s>") % (from_table, to_table))
  195. grass.verbose(_("Connect table <%s> to vector map <%s> at layer <%s>") %
  196. (to_table, map_name, layer))
  197. # and connect the new tables with the right layer
  198. try:
  199. grass.run_command('v.db.connect', flags='o', quiet=True,
  200. driver=dbconn['driver'], database=todb,
  201. map=map_name, key=values[2],
  202. layer=layer, table=to_table)
  203. except CalledModuleError:
  204. grass.fatal(_("Unable to connect table <%s> to vector map <%s>") %
  205. (to_table, map_name))
  206. grass.message(_("Vector map <%s> successfully unpacked") % map_name)
  207. if __name__ == "__main__":
  208. options, flags = grass.parser()
  209. atexit.register(cleanup)
  210. sys.exit(main())