v.import.py 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353
  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 is_projection_matching(OGRdatasource):
  125. """Returns True if current location projection
  126. matches dataset projection, otherwise False"""
  127. try:
  128. grass.run_command('v.in.ogr', input=OGRdatasource, flags='j',
  129. quiet=True)
  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('g.proj', epsg=options['epsg'],
  184. datum_trans=options['datum_trans'])
  185. if layers:
  186. vopts['layer'] = layers
  187. if output:
  188. vopts['output'] = output
  189. vopts['snap'] = options['snap']
  190. # try v.in.ogr directly
  191. if flags['o'] or is_projection_matching(OGRdatasource):
  192. try:
  193. grass.run_command('v.in.ogr', input=OGRdatasource,
  194. flags=vflags, overwrite=overwrite, **vopts)
  195. grass.message(
  196. _("Input <%s> successfully imported without reprojection") %
  197. OGRdatasource)
  198. return 0
  199. except CalledModuleError:
  200. grass.fatal(_("Unable to import <%s>") % OGRdatasource)
  201. grassenv = grass.gisenv()
  202. tgtloc = grassenv['LOCATION_NAME']
  203. # make sure target is not xy
  204. if grass.parse_command('g.proj', flags='g')['name'] == 'xy_location_unprojected':
  205. grass.fatal(
  206. _("Coordinate reference system not available for current location <%s>") %
  207. tgtloc)
  208. tgtmapset = grassenv['MAPSET']
  209. GISDBASE = grassenv['GISDBASE']
  210. TGTGISRC = os.environ['GISRC']
  211. SRCGISRC = grass.tempfile()
  212. TMPLOC = 'temp_import_location_' + str(os.getpid())
  213. f = open(SRCGISRC, 'w')
  214. f.write('MAPSET: PERMANENT\n')
  215. f.write('GISDBASE: %s\n' % GISDBASE)
  216. f.write('LOCATION_NAME: %s\n' % TMPLOC)
  217. f.write('GUI: text\n')
  218. f.close()
  219. tgtsrs = grass.read_command('g.proj', flags='j', quiet=True)
  220. # create temp location from input without import
  221. grass.verbose(_("Creating temporary location for <%s>...") % OGRdatasource)
  222. try:
  223. if OGRdatasource.lower().endswith("gml"):
  224. try:
  225. from osgeo import gdal
  226. except:
  227. grass.fatal(_("Unable to load GDAL Python bindings (requires package 'python-gdal' being installed)"))
  228. if int(gdal.VersionInfo('VERSION_NUM')) < GDAL_COMPUTE_VERSION(2, 4, 1):
  229. fix_gfsfile(OGRdatasource)
  230. grass.run_command('v.in.ogr', input=OGRdatasource,
  231. location=TMPLOC, flags='i',
  232. quiet=True, overwrite=overwrite, **vopts)
  233. except CalledModuleError:
  234. grass.fatal(_("Unable to create location from OGR datasource <%s>") % OGRdatasource)
  235. # switch to temp location
  236. os.environ['GISRC'] = str(SRCGISRC)
  237. if options['epsg']: # force given EPSG
  238. kwargs = {}
  239. if options['datum_trans']:
  240. kwargs['datum_trans'] = options['datum_trans']
  241. grass.run_command('g.proj', flags='c', epsg=options['epsg'], **kwargs)
  242. # print projection at verbose level
  243. grass.verbose(grass.read_command('g.proj', flags='p').rstrip(os.linesep))
  244. # make sure input is not xy
  245. if grass.parse_command('g.proj', flags='g')['name'] == 'xy_location_unprojected':
  246. grass.fatal(_("Coordinate reference system not available for input <%s>") % OGRdatasource)
  247. if options['extent'] == 'region':
  248. # switch to target location
  249. os.environ['GISRC'] = str(TGTGISRC)
  250. # v.in.region in tgt
  251. vreg = 'vreg_' + str(os.getpid())
  252. grass.run_command('v.in.region', output=vreg, quiet=True)
  253. # reproject to src
  254. # switch to temp location
  255. os.environ['GISRC'] = str(SRCGISRC)
  256. try:
  257. grass.run_command('v.proj', input=vreg, output=vreg,
  258. location=tgtloc, mapset=tgtmapset, quiet=True, overwrite=overwrite)
  259. except CalledModuleError:
  260. grass.fatal(_("Unable to reproject to source location"))
  261. # set region from region vector
  262. grass.run_command('g.region', res='1')
  263. grass.run_command('g.region', vector=vreg)
  264. # import into temp location
  265. grass.message(_("Importing <%s> ...") % OGRdatasource)
  266. try:
  267. if OGRdatasource.lower().endswith("gml"):
  268. try:
  269. from osgeo import gdal
  270. except:
  271. grass.fatal(_("Unable to load GDAL Python bindings (requires package 'python-gdal' being installed)"))
  272. if int(gdal.VersionInfo('VERSION_NUM')) < GDAL_COMPUTE_VERSION(2, 4, 1):
  273. fix_gfsfile(OGRdatasource)
  274. grass.run_command('v.in.ogr', input=OGRdatasource,
  275. flags=vflags, overwrite=overwrite, **vopts)
  276. except CalledModuleError:
  277. grass.fatal(_("Unable to import OGR datasource <%s>") % OGRdatasource)
  278. # if output is not define check source mapset
  279. if not output:
  280. output = grass.list_grouped('vector')['PERMANENT'][0]
  281. # switch to target location
  282. os.environ['GISRC'] = str(TGTGISRC)
  283. # check if map exists
  284. if not grass.overwrite() and \
  285. grass.find_file(output, element='vector', mapset='.')['mapset']:
  286. grass.fatal(_("option <%s>: <%s> exists.") % ('output', output))
  287. if options['extent'] == 'region':
  288. grass.run_command('g.remove', type='vector', name=vreg,
  289. flags='f', quiet=True)
  290. # v.proj
  291. grass.message(_("Reprojecting <%s>...") % output)
  292. try:
  293. grass.run_command('v.proj', location=TMPLOC,
  294. mapset='PERMANENT', input=output, overwrite=overwrite)
  295. except CalledModuleError:
  296. grass.fatal(_("Unable to to reproject vector <%s>") % output)
  297. return 0
  298. if __name__ == "__main__":
  299. options, flags = grass.parser()
  300. atexit.register(cleanup)
  301. sys.exit(main())