v.import.py 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396
  1. #!/usr/bin/env python3
  2. ############################################################################
  3. #
  4. # MODULE: v.import
  5. #
  6. # AUTHOR(S): Markus Metz
  7. #
  8. # PURPOSE: Import and reproject vector data on the fly
  9. #
  10. # COPYRIGHT: (C) 2015 by GRASS development team
  11. #
  12. # This program is free software under the GNU General
  13. # Public License (>=v2). Read the file COPYING that
  14. # comes with GRASS for details.
  15. #
  16. #############################################################################
  17. # %module
  18. # % description: Imports vector data into a GRASS vector map using OGR library and reprojects on the fly.
  19. # % keyword: vector
  20. # % keyword: import
  21. # % keyword: projection
  22. # %end
  23. # %option
  24. # % key: input
  25. # % type: string
  26. # % required: yes
  27. # % description: Name of OGR datasource to be imported
  28. # % gisprompt: old,datasource,datasource
  29. # % guisection: Input
  30. # %end
  31. # %option
  32. # % key: layer
  33. # % type: string
  34. # % multiple: yes
  35. # % description: OGR layer name. If not given, all available layers are imported
  36. # % guisection: Input
  37. # % gisprompt: old,datasource_layer,datasource_layer
  38. # %end
  39. # %option G_OPT_V_OUTPUT
  40. # % description: Name for output vector map (default: input)
  41. # % required: no
  42. # % guisection: Output
  43. # %end
  44. # %option
  45. # % key: extent
  46. # % type: string
  47. # % options: input,region
  48. # % answer: input
  49. # % description: Output vector map extent
  50. # % descriptions: input;extent of input map;region;extent of current region
  51. # % guisection: Output
  52. # %end
  53. # %option
  54. # % key: encoding
  55. # % type: string
  56. # % label: Encoding value for attribute data
  57. # % descriptions: Overrides encoding interpretation, useful when importing ESRI Shapefile
  58. # % guisection: Output
  59. # %end
  60. # %option
  61. # % key: snap
  62. # % type: double
  63. # % label: Snapping threshold for boundaries (map units)
  64. # % description: A suitable threshold is estimated during import
  65. # % answer: -1
  66. # % guisection: Output
  67. # %end
  68. # %option
  69. # % key: epsg
  70. # % type: integer
  71. # % options: 1-1000000
  72. # % guisection: Input SRS
  73. # % description: EPSG projection code
  74. # %end
  75. # %option
  76. # % key: datum_trans
  77. # % type: integer
  78. # % options: -1-100
  79. # % guisection: Input SRS
  80. # % label: Index number of datum transform parameters
  81. # % description: -1 to list available datum transform parameters
  82. # %end
  83. # %flag
  84. # % key: f
  85. # % description: List supported OGR formats and exit
  86. # % suppress_required: yes
  87. # %end
  88. # %flag
  89. # % key: l
  90. # % description: List available OGR layers in data source and exit
  91. # %end
  92. # %flag
  93. # % key: o
  94. # % label: Override projection check (use current location's projection)
  95. # % description: Assume that the dataset has the same projection as the current location
  96. # %end
  97. import sys
  98. import os
  99. import atexit
  100. import xml.etree.ElementTree as ET # only needed for GDAL version < 2.4.1
  101. import re # only needed for GDAL version < 2.4.1
  102. import grass.script as grass
  103. from grass.exceptions import CalledModuleError
  104. # initialize global vars
  105. TMPLOC = None
  106. SRCGISRC = None
  107. TGTGISRC = None
  108. GISDBASE = None
  109. def cleanup():
  110. if TGTGISRC:
  111. os.environ["GISRC"] = str(TGTGISRC)
  112. # remove temp location
  113. if TMPLOC:
  114. grass.try_rmdir(os.path.join(GISDBASE, TMPLOC))
  115. if SRCGISRC:
  116. grass.try_remove(SRCGISRC)
  117. def gdal_version():
  118. """Returns the GDAL version as tuple"""
  119. version = grass.parse_command("g.version", flags="reg")["gdal"]
  120. return version
  121. def GDAL_COMPUTE_VERSION(maj, min, rev):
  122. return (maj) * 1000000 + (min) * 10000 + (rev) * 100
  123. def is_projection_matching(OGRdatasource, layer):
  124. """Returns True if current location projection
  125. matches dataset projection, otherwise False"""
  126. try:
  127. grass.run_command(
  128. "v.in.ogr", input=OGRdatasource, layer=layer, flags="j", quiet=True
  129. )
  130. return True
  131. except CalledModuleError:
  132. return False
  133. def fix_gfsfile(input):
  134. """Fixes the gfs file of an gml file by adding the SRSName
  135. :param input: gml file name to import with v.import
  136. :type input: str
  137. """
  138. # get srs string from gml file
  139. gmltree = ET.parse(input)
  140. gmlroot = gmltree.getroot()
  141. gmlstring = ET.tostring(gmlroot).decode("utf-8")
  142. srs_str = re.search(r"srsname=\"(.*?)\"", gmlstring.lower()).groups()[0]
  143. # set srs string in gfs file
  144. gml = os.path.basename(input).split(".")[-1]
  145. gfsfile = input.replace(gml, "gfs")
  146. if os.path.isfile(gfsfile):
  147. tree = ET.parse(gfsfile)
  148. root = tree.getroot()
  149. gfsstring = ET.tostring(root).decode("utf-8")
  150. if "srsname" not in gfsstring.lower():
  151. for featClass in root.findall("GMLFeatureClass"):
  152. ET.SubElement(featClass, "SRSName").text = srs_str
  153. tree.write(gfsfile)
  154. def main():
  155. global TMPLOC, SRCGISRC, TGTGISRC, GISDBASE
  156. overwrite = grass.overwrite()
  157. # list formats and exit
  158. if flags["f"]:
  159. grass.run_command("v.in.ogr", flags="f")
  160. return 0
  161. # list layers and exit
  162. if flags["l"]:
  163. try:
  164. grass.run_command("v.in.ogr", flags="l", input=options["input"])
  165. except CalledModuleError:
  166. return 1
  167. return 0
  168. OGRdatasource = options["input"]
  169. output = options["output"]
  170. layers = options["layer"]
  171. vflags = ""
  172. if options["extent"] == "region":
  173. vflags += "r"
  174. if flags["o"]:
  175. vflags += "o"
  176. vopts = {}
  177. if options["encoding"]:
  178. vopts["encoding"] = options["encoding"]
  179. if options["datum_trans"] and options["datum_trans"] == "-1":
  180. # list datum transform parameters
  181. if not options["epsg"]:
  182. grass.fatal(_("Missing value for parameter <%s>") % "epsg")
  183. return grass.run_command(
  184. "g.proj", epsg=options["epsg"], datum_trans=options["datum_trans"]
  185. )
  186. if layers:
  187. vopts["layer"] = layers
  188. if output:
  189. vopts["output"] = output
  190. vopts["snap"] = options["snap"]
  191. # try v.in.ogr directly
  192. if flags["o"] or is_projection_matching(OGRdatasource, layers):
  193. try:
  194. grass.run_command(
  195. "v.in.ogr",
  196. input=OGRdatasource,
  197. flags=vflags,
  198. overwrite=overwrite,
  199. **vopts,
  200. )
  201. grass.message(
  202. _("Input <%s> successfully imported without reprojection")
  203. % OGRdatasource
  204. )
  205. return 0
  206. except CalledModuleError:
  207. grass.fatal(_("Unable to import <%s>") % OGRdatasource)
  208. grassenv = grass.gisenv()
  209. tgtloc = grassenv["LOCATION_NAME"]
  210. # make sure target is not xy
  211. if grass.parse_command("g.proj", flags="g")["name"] == "xy_location_unprojected":
  212. grass.fatal(
  213. _("Coordinate reference system not available for current location <%s>")
  214. % tgtloc
  215. )
  216. tgtmapset = grassenv["MAPSET"]
  217. GISDBASE = grassenv["GISDBASE"]
  218. TGTGISRC = os.environ["GISRC"]
  219. SRCGISRC = grass.tempfile()
  220. TMPLOC = grass.append_node_pid("tmp_v_import_location")
  221. f = open(SRCGISRC, "w")
  222. f.write("MAPSET: PERMANENT\n")
  223. f.write("GISDBASE: %s\n" % GISDBASE)
  224. f.write("LOCATION_NAME: %s\n" % TMPLOC)
  225. f.write("GUI: text\n")
  226. f.close()
  227. tgtsrs = grass.read_command("g.proj", flags="j", quiet=True)
  228. # create temp location from input without import
  229. grass.verbose(_("Creating temporary location for <%s>...") % OGRdatasource)
  230. try:
  231. if OGRdatasource.lower().endswith("gml"):
  232. try:
  233. from osgeo import gdal
  234. except:
  235. grass.fatal(
  236. _(
  237. "Unable to load GDAL Python bindings (requires package 'python-gdal' being installed)"
  238. )
  239. )
  240. if int(gdal.VersionInfo("VERSION_NUM")) < GDAL_COMPUTE_VERSION(2, 4, 1):
  241. fix_gfsfile(OGRdatasource)
  242. grass.run_command(
  243. "v.in.ogr",
  244. input=OGRdatasource,
  245. location=TMPLOC,
  246. flags="i",
  247. quiet=True,
  248. overwrite=overwrite,
  249. **vopts,
  250. )
  251. except CalledModuleError:
  252. grass.fatal(
  253. _("Unable to create location from OGR datasource <%s>") % OGRdatasource
  254. )
  255. # switch to temp location
  256. os.environ["GISRC"] = str(SRCGISRC)
  257. if options["epsg"]: # force given EPSG
  258. kwargs = {}
  259. if options["datum_trans"]:
  260. kwargs["datum_trans"] = options["datum_trans"]
  261. grass.run_command("g.proj", flags="c", epsg=options["epsg"], **kwargs)
  262. # print projection at verbose level
  263. grass.verbose(grass.read_command("g.proj", flags="p").rstrip(os.linesep))
  264. # make sure input is not xy
  265. if grass.parse_command("g.proj", flags="g")["name"] == "xy_location_unprojected":
  266. grass.fatal(
  267. _("Coordinate reference system not available for input <%s>")
  268. % OGRdatasource
  269. )
  270. if options["extent"] == "region":
  271. # switch to target location
  272. os.environ["GISRC"] = str(TGTGISRC)
  273. # v.in.region in tgt
  274. vreg = grass.append_node_pid("tmp_v_import_region")
  275. grass.run_command("v.in.region", output=vreg, quiet=True)
  276. # reproject to src
  277. # switch to temp location
  278. os.environ["GISRC"] = str(SRCGISRC)
  279. try:
  280. grass.run_command(
  281. "v.proj",
  282. input=vreg,
  283. output=vreg,
  284. location=tgtloc,
  285. mapset=tgtmapset,
  286. quiet=True,
  287. overwrite=overwrite,
  288. )
  289. except CalledModuleError:
  290. grass.fatal(_("Unable to reproject to source location"))
  291. # set region from region vector
  292. grass.run_command("g.region", res="1")
  293. grass.run_command("g.region", vector=vreg)
  294. # import into temp location
  295. grass.message(_("Importing <%s> ...") % OGRdatasource)
  296. try:
  297. if OGRdatasource.lower().endswith("gml"):
  298. try:
  299. from osgeo import gdal
  300. except:
  301. grass.fatal(
  302. _(
  303. "Unable to load GDAL Python bindings (requires package 'python-gdal' being installed)"
  304. )
  305. )
  306. if int(gdal.VersionInfo("VERSION_NUM")) < GDAL_COMPUTE_VERSION(2, 4, 1):
  307. fix_gfsfile(OGRdatasource)
  308. grass.run_command(
  309. "v.in.ogr", input=OGRdatasource, flags=vflags, overwrite=overwrite, **vopts
  310. )
  311. except CalledModuleError:
  312. grass.fatal(_("Unable to import OGR datasource <%s>") % OGRdatasource)
  313. # if output is not define check source mapset
  314. if not output:
  315. output = grass.list_grouped("vector")["PERMANENT"][0]
  316. # switch to target location
  317. os.environ["GISRC"] = str(TGTGISRC)
  318. # check if map exists
  319. if (
  320. not grass.overwrite()
  321. and grass.find_file(output, element="vector", mapset=".")["mapset"]
  322. ):
  323. grass.fatal(_("option <%s>: <%s> exists.") % ("output", output))
  324. if options["extent"] == "region":
  325. grass.run_command("g.remove", type="vector", name=vreg, flags="f", quiet=True)
  326. # v.proj
  327. grass.message(_("Reprojecting <%s>...") % output)
  328. try:
  329. grass.run_command(
  330. "v.proj",
  331. location=TMPLOC,
  332. mapset="PERMANENT",
  333. input=output,
  334. overwrite=overwrite,
  335. )
  336. except CalledModuleError:
  337. grass.fatal(_("Unable to to reproject vector <%s>") % output)
  338. return 0
  339. if __name__ == "__main__":
  340. options, flags = grass.parser()
  341. atexit.register(cleanup)
  342. sys.exit(main())