v.unpack.py 9.5 KB

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