stds_import.py 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600
  1. """
  2. Space time dataset import functions
  3. Usage:
  4. .. code-block:: python
  5. import grass.temporal as tgis
  6. input="/tmp/temp_1950_2012.tar.gz"
  7. output="temp_1950_2012"
  8. directory="/tmp"
  9. title="My new dataset"
  10. descr="May new shiny dataset"
  11. location=None
  12. link=True
  13. exp=True
  14. overr=False
  15. create=False
  16. tgis.import_stds(input, output, directory, title, descr, location,
  17. link, exp, overr, create, "strds")
  18. (C) 2012-2013 by the GRASS Development Team
  19. This program is free software under the GNU General Public
  20. License (>=v2). Read the file COPYING that comes with GRASS
  21. for details.
  22. :authors: Soeren Gebbert
  23. """
  24. import os
  25. import os.path
  26. import tarfile
  27. from .core import get_current_mapset, get_tgis_message_interface
  28. from .register import register_maps_in_space_time_dataset
  29. from .factory import dataset_factory
  30. import grass.script as gscript
  31. from grass.exceptions import CalledModuleError
  32. proj_file_name = "proj.txt"
  33. init_file_name = "init.txt"
  34. list_file_name = "list.txt"
  35. # This global variable is for unique vector map export,
  36. # since single vector maps may have several layer
  37. # and therefore several attribute tables
  38. imported_maps = {}
  39. ############################################################################
  40. def _import_raster_maps_from_gdal(
  41. maplist, overr, exp, location, link, format_, set_current_region=False, memory=300
  42. ):
  43. impflags = ""
  44. if overr:
  45. impflags += "o"
  46. if exp or location:
  47. impflags += "e"
  48. for row in maplist:
  49. name = row["name"]
  50. if format_ == "GTiff":
  51. filename = row["filename"] + ".tif"
  52. elif format_ == "AAIGrid":
  53. filename = row["filename"] + ".asc"
  54. if not overr:
  55. impflags += "o"
  56. try:
  57. if link:
  58. gscript.run_command(
  59. "r.external",
  60. input=filename,
  61. output=name,
  62. flags=impflags,
  63. overwrite=gscript.overwrite(),
  64. )
  65. else:
  66. gscript.run_command(
  67. "r.in.gdal",
  68. input=filename,
  69. output=name,
  70. memory=memory,
  71. flags=impflags,
  72. overwrite=gscript.overwrite(),
  73. )
  74. except CalledModuleError:
  75. gscript.fatal(
  76. _("Unable to import/link raster map <%s> from file" " %s.")
  77. % (name, filename)
  78. )
  79. # Set the color rules if present
  80. filename = row["filename"] + ".color"
  81. if os.path.isfile(filename):
  82. try:
  83. gscript.run_command(
  84. "r.colors", map=name, rules=filename, overwrite=gscript.overwrite()
  85. )
  86. except CalledModuleError:
  87. gscript.fatal(
  88. _("Unable to set the color rules for " "raster map <%s>.") % name
  89. )
  90. # Set the computational region from the last map imported
  91. if set_current_region is True:
  92. gscript.run_command("g.region", raster=name)
  93. ############################################################################
  94. def _import_raster_maps(maplist, set_current_region=False):
  95. # We need to disable the projection check because of its
  96. # simple implementation
  97. impflags = "o"
  98. for row in maplist:
  99. name = row["name"]
  100. filename = row["filename"] + ".pack"
  101. try:
  102. gscript.run_command(
  103. "r.unpack",
  104. input=filename,
  105. output=name,
  106. flags=impflags,
  107. overwrite=gscript.overwrite(),
  108. verbose=True,
  109. )
  110. except CalledModuleError:
  111. gscript.fatal(
  112. _("Unable to unpack raster map <%s> from file " "%s.")
  113. % (name, filename)
  114. )
  115. # Set the computational region from the last map imported
  116. if set_current_region is True:
  117. gscript.run_command("g.region", raster=name)
  118. ############################################################################
  119. def _import_vector_maps_from_gml(maplist, overr, exp, location, link):
  120. impflags = "o"
  121. if exp or location:
  122. impflags += "e"
  123. for row in maplist:
  124. name = row["name"]
  125. filename = row["filename"] + ".xml"
  126. try:
  127. gscript.run_command(
  128. "v.in.ogr",
  129. input=filename,
  130. output=name,
  131. flags=impflags,
  132. overwrite=gscript.overwrite(),
  133. )
  134. except CalledModuleError:
  135. gscript.fatal(
  136. _("Unable to import vector map <%s> from file " "%s.")
  137. % (name, filename)
  138. )
  139. ############################################################################
  140. def _import_vector_maps(maplist):
  141. # We need to disable the projection check because of its
  142. # simple implementation
  143. impflags = "o"
  144. for row in maplist:
  145. # Separate the name from the layer
  146. name = row["name"].split(":")[0]
  147. # Import only unique maps
  148. if name in imported_maps:
  149. continue
  150. filename = row["filename"] + ".pack"
  151. try:
  152. gscript.run_command(
  153. "v.unpack",
  154. input=filename,
  155. output=name,
  156. flags=impflags,
  157. overwrite=gscript.overwrite(),
  158. verbose=True,
  159. )
  160. except CalledModuleError:
  161. gscript.fatal(
  162. _("Unable to unpack vector map <%s> from file " "%s.")
  163. % (name, filename)
  164. )
  165. imported_maps[name] = name
  166. ############################################################################
  167. def import_stds(
  168. input,
  169. output,
  170. directory,
  171. title=None,
  172. descr=None,
  173. location=None,
  174. link=False,
  175. exp=False,
  176. overr=False,
  177. create=False,
  178. stds_type="strds",
  179. base=None,
  180. set_current_region=False,
  181. memory=300,
  182. ):
  183. """Import space time datasets of type raster and vector
  184. :param input: Name of the input archive file
  185. :param output: The name of the output space time dataset
  186. :param directory: The extraction directory
  187. :param title: The title of the new created space time dataset
  188. :param descr: The description of the new created
  189. space time dataset
  190. :param location: The name of the location that should be created,
  191. maps are imported into this location
  192. :param link: Switch to link raster maps instead importing them
  193. :param exp: Extend location extents based on new dataset
  194. :param overr: Override projection (use location's projection)
  195. :param create: Create the location specified by the "location"
  196. parameter and exit.
  197. Do not import the space time datasets.
  198. :param stds_type: The type of the space time dataset that
  199. should be imported
  200. :param base: The base name of the new imported maps, it will be
  201. extended using a numerical index.
  202. :param memory: Cache size for raster rows, used in r.in.gdal
  203. """
  204. old_state = gscript.raise_on_error
  205. gscript.set_raise_on_error(True)
  206. # Check if input file and extraction directory exits
  207. if not os.path.exists(input):
  208. gscript.fatal(_("Space time raster dataset archive <%s> not found") % input)
  209. if not create and not os.path.exists(directory):
  210. gscript.fatal(_("Extraction directory <%s> not found") % directory)
  211. tar = tarfile.open(name=input, mode="r")
  212. # Check for important files
  213. msgr = get_tgis_message_interface()
  214. msgr.message(
  215. _(
  216. "Checking validity of input file (size: %0.1f MB). Make take a while..."
  217. % (os.path.getsize(input) / (1024 * 1024.0))
  218. )
  219. )
  220. members = tar.getnames()
  221. # Make sure that the basenames of the files are used for comparison
  222. member_basenames = [os.path.basename(name) for name in members]
  223. if init_file_name not in member_basenames:
  224. gscript.fatal(_("Unable to find init file <%s>") % init_file_name)
  225. if list_file_name not in member_basenames:
  226. gscript.fatal(_("Unable to find list file <%s>") % list_file_name)
  227. if proj_file_name not in member_basenames:
  228. gscript.fatal(_("Unable to find projection file <%s>") % proj_file_name)
  229. msgr.message(_("Extracting data..."))
  230. tar.extractall(path=directory)
  231. tar.close()
  232. # We use a new list file name for map registration
  233. new_list_file_name = list_file_name + "_new"
  234. # Save current working directory path
  235. old_cwd = os.getcwd()
  236. # Switch into the data directory
  237. os.chdir(directory)
  238. # Check projection information
  239. if not location:
  240. temp_name = gscript.tempfile()
  241. temp_file = open(temp_name, "w")
  242. proj_name = os.path.abspath(proj_file_name)
  243. # We need to convert projection strings generated
  244. # from other programs than g.proj into
  245. # new line format so that the grass file comparison function
  246. # can be used to compare the projections
  247. proj_name_tmp = temp_name + "_in_projection"
  248. proj_file = open(proj_name, "r")
  249. proj_content = proj_file.read()
  250. proj_content = proj_content.replace(" +", "\n+")
  251. proj_content = proj_content.replace("\t+", "\n+")
  252. proj_file.close()
  253. proj_file = open(proj_name_tmp, "w")
  254. proj_file.write(proj_content)
  255. proj_file.close()
  256. p = gscript.start_command("g.proj", flags="j", stdout=temp_file)
  257. p.communicate()
  258. temp_file.close()
  259. if not gscript.compare_key_value_text_files(temp_name, proj_name_tmp, sep="="):
  260. if overr:
  261. gscript.warning(
  262. _("Projection information does not match. " "Proceeding...")
  263. )
  264. else:
  265. diff = "".join(gscript.diff_files(temp_name, proj_name))
  266. gscript.warning(
  267. _(
  268. "Difference between PROJ_INFO file of "
  269. "imported map and of current location:"
  270. "\n{diff}"
  271. ).format(diff=diff)
  272. )
  273. gscript.fatal(_("Projection information does not match. " "Aborting."))
  274. # Create a new location based on the projection information and switch
  275. # into it
  276. old_env = gscript.gisenv()
  277. if location:
  278. try:
  279. proj4_string = open(proj_file_name, "r").read()
  280. gscript.create_location(
  281. dbase=old_env["GISDBASE"], location=location, proj4=proj4_string
  282. )
  283. # Just create a new location and return
  284. if create:
  285. os.chdir(old_cwd)
  286. return
  287. except Exception as e:
  288. gscript.fatal(
  289. _("Unable to create location %(l)s. Reason: %(e)s")
  290. % {"l": location, "e": str(e)}
  291. )
  292. # Switch to the new created location
  293. try:
  294. gscript.run_command(
  295. "g.mapset",
  296. mapset="PERMANENT",
  297. location=location,
  298. dbase=old_env["GISDBASE"],
  299. )
  300. except CalledModuleError:
  301. gscript.fatal(_("Unable to switch to location %s") % location)
  302. # create default database connection
  303. try:
  304. gscript.run_command("t.connect", flags="d")
  305. except CalledModuleError:
  306. gscript.fatal(
  307. _("Unable to create default temporal database " "in new location %s")
  308. % location
  309. )
  310. try:
  311. # Make sure the temporal database exists
  312. from .core import init
  313. init()
  314. fs = "|"
  315. maplist = []
  316. mapset = get_current_mapset()
  317. list_file = open(list_file_name, "r")
  318. new_list_file = open(new_list_file_name, "w")
  319. # get number of lines to correctly form the suffix
  320. max_count = -1
  321. for max_count, l in enumerate(list_file):
  322. pass
  323. max_count += 1
  324. list_file.seek(0)
  325. # Read the map list from file
  326. line_count = 0
  327. while True:
  328. line = list_file.readline()
  329. if not line:
  330. break
  331. line_list = line.split(fs)
  332. # The filename is actually the base name of the map
  333. # that must be extended by the file suffix
  334. filename = line_list[0].strip().split(":")[0]
  335. if base:
  336. mapname = "%s_%s" % (
  337. base,
  338. gscript.get_num_suffix(line_count + 1, max_count),
  339. )
  340. mapid = "%s@%s" % (mapname, mapset)
  341. else:
  342. mapname = filename
  343. mapid = mapname + "@" + mapset
  344. row = {}
  345. row["filename"] = filename
  346. row["name"] = mapname
  347. row["id"] = mapid
  348. row["start"] = line_list[1].strip()
  349. row["end"] = line_list[2].strip()
  350. new_list_file.write(
  351. "%s%s%s%s%s\n" % (mapname, fs, row["start"], fs, row["end"])
  352. )
  353. maplist.append(row)
  354. line_count += 1
  355. list_file.close()
  356. new_list_file.close()
  357. # Read the init file
  358. fs = "="
  359. init = {}
  360. init_file = open(init_file_name, "r")
  361. while True:
  362. line = init_file.readline()
  363. if not line:
  364. break
  365. kv = line.split(fs)
  366. init[kv[0]] = kv[1].strip()
  367. init_file.close()
  368. if (
  369. "temporal_type" not in init
  370. or "semantic_type" not in init
  371. or "number_of_maps" not in init
  372. ):
  373. gscript.fatal(
  374. _("Key words %(t)s, %(s)s or %(n)s not found in init" " file.")
  375. % {"t": "temporal_type", "s": "semantic_type", "n": "number_of_maps"}
  376. )
  377. if line_count != int(init["number_of_maps"]):
  378. gscript.fatal(_("Number of maps mismatch in init and list file."))
  379. format_ = "GTiff"
  380. type_ = "strds"
  381. if "stds_type" in init:
  382. type_ = init["stds_type"]
  383. if "format" in init:
  384. format_ = init["format"]
  385. if stds_type != type_:
  386. gscript.fatal(_("The archive file is of wrong space time dataset" " type"))
  387. # Check the existence of the files
  388. if format_ == "GTiff":
  389. for row in maplist:
  390. filename = row["filename"] + ".tif"
  391. if not os.path.exists(filename):
  392. gscript.fatal(
  393. _("Unable to find GeoTIFF raster file " "<%s> in archive.")
  394. % filename
  395. )
  396. elif format_ == "AAIGrid":
  397. for row in maplist:
  398. filename = row["filename"] + ".asc"
  399. if not os.path.exists(filename):
  400. gscript.fatal(
  401. _("Unable to find AAIGrid raster file " "<%s> in archive.")
  402. % filename
  403. )
  404. elif format_ == "GML":
  405. for row in maplist:
  406. filename = row["filename"] + ".xml"
  407. if not os.path.exists(filename):
  408. gscript.fatal(
  409. _("Unable to find GML vector file " "<%s> in archive.")
  410. % filename
  411. )
  412. elif format_ == "pack":
  413. for row in maplist:
  414. if type_ == "stvds":
  415. filename = str(row["filename"].split(":")[0]) + ".pack"
  416. else:
  417. filename = row["filename"] + ".pack"
  418. if not os.path.exists(filename):
  419. gscript.fatal(
  420. _("Unable to find GRASS package file " "<%s> in archive.")
  421. % filename
  422. )
  423. else:
  424. gscript.fatal(_("Unsupported input format"))
  425. # Check the space time dataset
  426. id = output + "@" + mapset
  427. sp = dataset_factory(type_, id)
  428. if sp.is_in_db() and gscript.overwrite() is False:
  429. gscript.fatal(
  430. _(
  431. "Space time %(t)s dataset <%(sp)s> is already in"
  432. " the database. Use the overwrite flag."
  433. )
  434. % {"t": type_, "sp": sp.get_id()}
  435. )
  436. # Import the maps
  437. if type_ == "strds":
  438. if format_ == "GTiff" or format_ == "AAIGrid":
  439. _import_raster_maps_from_gdal(
  440. maplist,
  441. overr,
  442. exp,
  443. location,
  444. link,
  445. format_,
  446. set_current_region,
  447. memory,
  448. )
  449. if format_ == "pack":
  450. _import_raster_maps(maplist, set_current_region)
  451. elif type_ == "stvds":
  452. if format_ == "GML":
  453. _import_vector_maps_from_gml(maplist, overr, exp, location, link)
  454. if format_ == "pack":
  455. _import_vector_maps(maplist)
  456. # Create the space time dataset
  457. if sp.is_in_db() and gscript.overwrite() is True:
  458. gscript.info(
  459. _(
  460. "Overwrite space time %(sp)s dataset "
  461. "<%(id)s> and unregister all maps."
  462. )
  463. % {"sp": sp.get_new_map_instance(None).get_type(), "id": sp.get_id()}
  464. )
  465. sp.delete()
  466. sp = sp.get_new_instance(id)
  467. temporal_type = init["temporal_type"]
  468. semantic_type = init["semantic_type"]
  469. relative_time_unit = None
  470. if temporal_type == "relative":
  471. if "relative_time_unit" not in init:
  472. gscript.fatal(
  473. _("Key word %s not found in init file.") % ("relative_time_unit")
  474. )
  475. relative_time_unit = init["relative_time_unit"]
  476. sp.set_relative_time_unit(relative_time_unit)
  477. gscript.verbose(
  478. _("Create space time %s dataset.")
  479. % sp.get_new_map_instance(None).get_type()
  480. )
  481. sp.set_initial_values(
  482. temporal_type=temporal_type,
  483. semantic_type=semantic_type,
  484. title=title,
  485. description=descr,
  486. )
  487. sp.insert()
  488. # register the maps
  489. fs = "|"
  490. register_maps_in_space_time_dataset(
  491. type=sp.get_new_map_instance(None).get_type(),
  492. name=output,
  493. file=new_list_file_name,
  494. start="file",
  495. end="file",
  496. unit=relative_time_unit,
  497. dbif=None,
  498. fs=fs,
  499. update_cmd_list=False,
  500. )
  501. os.chdir(old_cwd)
  502. # Make sure the location is switched back correctly
  503. finally:
  504. if location:
  505. # Switch to the old location
  506. try:
  507. gscript.run_command(
  508. "g.mapset",
  509. mapset=old_env["MAPSET"],
  510. location=old_env["LOCATION_NAME"],
  511. gisdbase=old_env["GISDBASE"],
  512. )
  513. except CalledModuleError:
  514. gscript.warning(_("Switching to original location failed"))
  515. gscript.set_raise_on_error(old_state)