v.import.py 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344
  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. """
  120. version = grass.parse_command('g.version', flags='reg')['gdal']
  121. return version
  122. def GDAL_COMPUTE_VERSION(maj, min, rev):
  123. return ((maj) * 1000000 + (min) * 10000 + (rev) * 100)
  124. def fix_gfsfile(input):
  125. """Fixes the gfs file of an gml file by adding the SRSName
  126. :param input: gml file name to import with v.import
  127. :type input: str
  128. """
  129. # get srs string from gml file
  130. gmltree = ET.parse(input)
  131. gmlroot = gmltree.getroot()
  132. gmlstring = ET.tostring(gmlroot).decode('utf-8')
  133. srs_str = re.search(r"srsname=\"(.*?)\"", gmlstring.lower()).groups()[0]
  134. # set srs string in gfs file
  135. gml = os.path.basename(input).split('.')[-1]
  136. gfsfile = input.replace(gml, 'gfs')
  137. if os.path.isfile(gfsfile):
  138. tree = ET.parse(gfsfile)
  139. root = tree.getroot()
  140. gfsstring = ET.tostring(root).decode('utf-8')
  141. if not "srsname" in gfsstring.lower():
  142. for featClass in root.findall('GMLFeatureClass'):
  143. ET.SubElement(featClass, 'SRSName').text = srs_str
  144. tree.write(gfsfile)
  145. def main():
  146. global TMPLOC, SRCGISRC, TGTGISRC, GISDBASE
  147. overwrite = grass.overwrite()
  148. # list formats and exit
  149. if flags['f']:
  150. grass.run_command('v.in.ogr', flags='f')
  151. return 0
  152. # list layers and exit
  153. if flags['l']:
  154. try:
  155. grass.run_command('v.in.ogr', flags='l', input=options['input'])
  156. except CalledModuleError:
  157. return 1
  158. return 0
  159. OGRdatasource = options['input']
  160. output = options['output']
  161. layers = options['layer']
  162. vflags = ''
  163. if options['extent'] == 'region':
  164. vflags += 'r'
  165. if flags['o']:
  166. vflags += 'o'
  167. vopts = {}
  168. if options['encoding']:
  169. vopts['encoding'] = options['encoding']
  170. if options['datum_trans'] and options['datum_trans'] == '-1':
  171. # list datum transform parameters
  172. if not options['epsg']:
  173. grass.fatal(_("Missing value for parameter <%s>") % 'epsg')
  174. return grass.run_command('g.proj', epsg=options['epsg'],
  175. datum_trans=options['datum_trans'])
  176. if layers:
  177. vopts['layer'] = layers
  178. if output:
  179. vopts['output'] = output
  180. vopts['snap'] = options['snap']
  181. # try v.in.ogr directly
  182. if flags['o'] or grass.run_command('v.in.ogr', input=OGRdatasource, flags='j',
  183. errors='status', quiet=True, overwrite=overwrite,
  184. **vopts) == 0:
  185. try:
  186. grass.run_command('v.in.ogr', input=OGRdatasource,
  187. flags=vflags, overwrite=overwrite, **vopts)
  188. grass.message(
  189. _("Input <%s> successfully imported without reprojection") %
  190. OGRdatasource)
  191. return 0
  192. except CalledModuleError:
  193. grass.fatal(_("Unable to import <%s>") % OGRdatasource)
  194. grassenv = grass.gisenv()
  195. tgtloc = grassenv['LOCATION_NAME']
  196. # make sure target is not xy
  197. if grass.parse_command('g.proj', flags='g')['name'] == 'xy_location_unprojected':
  198. grass.fatal(
  199. _("Coordinate reference system not available for current location <%s>") %
  200. tgtloc)
  201. tgtmapset = grassenv['MAPSET']
  202. GISDBASE = grassenv['GISDBASE']
  203. TGTGISRC = os.environ['GISRC']
  204. SRCGISRC = grass.tempfile()
  205. TMPLOC = 'temp_import_location_' + str(os.getpid())
  206. f = open(SRCGISRC, 'w')
  207. f.write('MAPSET: PERMANENT\n')
  208. f.write('GISDBASE: %s\n' % GISDBASE)
  209. f.write('LOCATION_NAME: %s\n' % TMPLOC)
  210. f.write('GUI: text\n')
  211. f.close()
  212. tgtsrs = grass.read_command('g.proj', flags='j', quiet=True)
  213. # create temp location from input without import
  214. grass.verbose(_("Creating temporary location for <%s>...") % OGRdatasource)
  215. try:
  216. if OGRdatasource.lower().endswith("gml"):
  217. try:
  218. from osgeo import gdal
  219. except:
  220. grass.fatal(_("Unable to load GDAL Python bindings (requires package 'python-gdal' being installed)"))
  221. if int(gdal.VersionInfo('VERSION_NUM')) < GDAL_COMPUTE_VERSION(2, 4, 1):
  222. fix_gfsfile(OGRdatasource)
  223. grass.run_command('v.in.ogr', input=OGRdatasource,
  224. location=TMPLOC, flags='i',
  225. quiet=True, overwrite=overwrite, **vopts)
  226. except CalledModuleError:
  227. grass.fatal(_("Unable to create location from OGR datasource <%s>") % OGRdatasource)
  228. # switch to temp location
  229. os.environ['GISRC'] = str(SRCGISRC)
  230. if options['epsg']: # force given EPSG
  231. kwargs = {}
  232. if options['datum_trans']:
  233. kwargs['datum_trans'] = options['datum_trans']
  234. grass.run_command('g.proj', flags='c', epsg=options['epsg'], **kwargs)
  235. # print projection at verbose level
  236. grass.verbose(grass.read_command('g.proj', flags='p').rstrip(os.linesep))
  237. # make sure input is not xy
  238. if grass.parse_command('g.proj', flags='g')['name'] == 'xy_location_unprojected':
  239. grass.fatal(_("Coordinate reference system not available for input <%s>") % OGRdatasource)
  240. if options['extent'] == 'region':
  241. # switch to target location
  242. os.environ['GISRC'] = str(TGTGISRC)
  243. # v.in.region in tgt
  244. vreg = 'vreg_' + str(os.getpid())
  245. grass.run_command('v.in.region', output=vreg, quiet=True)
  246. # reproject to src
  247. # switch to temp location
  248. os.environ['GISRC'] = str(SRCGISRC)
  249. try:
  250. grass.run_command('v.proj', input=vreg, output=vreg,
  251. location=tgtloc, mapset=tgtmapset, quiet=True, overwrite=overwrite)
  252. except CalledModuleError:
  253. grass.fatal(_("Unable to reproject to source location"))
  254. # set region from region vector
  255. grass.run_command('g.region', res='1')
  256. grass.run_command('g.region', vector=vreg)
  257. # import into temp location
  258. grass.message(_("Importing <%s> ...") % OGRdatasource)
  259. try:
  260. if OGRdatasource.lower().endswith("gml"):
  261. try:
  262. from osgeo import gdal
  263. except:
  264. grass.fatal(_("Unable to load GDAL Python bindings (requires package 'python-gdal' being installed)"))
  265. if int(gdal.VersionInfo('VERSION_NUM')) < GDAL_COMPUTE_VERSION(2, 4, 1):
  266. fix_gfsfile(OGRdatasource)
  267. grass.run_command('v.in.ogr', input=OGRdatasource,
  268. flags=vflags, overwrite=overwrite, **vopts)
  269. except CalledModuleError:
  270. grass.fatal(_("Unable to import OGR datasource <%s>") % OGRdatasource)
  271. # if output is not define check source mapset
  272. if not output:
  273. output = grass.list_grouped('vector')['PERMANENT'][0]
  274. # switch to target location
  275. os.environ['GISRC'] = str(TGTGISRC)
  276. # check if map exists
  277. if not grass.overwrite() and \
  278. grass.find_file(output, element='vector', mapset='.')['mapset']:
  279. grass.fatal(_("option <%s>: <%s> exists.") % ('output', output))
  280. if options['extent'] == 'region':
  281. grass.run_command('g.remove', type='vector', name=vreg,
  282. flags='f', quiet=True)
  283. # v.proj
  284. grass.message(_("Reprojecting <%s>...") % output)
  285. try:
  286. grass.run_command('v.proj', location=TMPLOC,
  287. mapset='PERMANENT', input=output, overwrite=overwrite)
  288. except CalledModuleError:
  289. grass.fatal(_("Unable to to reproject vector <%s>") % output)
  290. return 0
  291. if __name__ == "__main__":
  292. options, flags = grass.parser()
  293. atexit.register(cleanup)
  294. sys.exit(main())